1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.settingslib.accessibility; 18 19 import android.accessibilityservice.AccessibilityServiceInfo; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.pm.ResolveInfo; 23 import android.content.pm.ServiceInfo; 24 import android.content.res.Configuration; 25 import android.content.res.Resources; 26 import android.os.UserHandle; 27 import android.provider.Settings; 28 import android.text.TextUtils; 29 import android.util.ArraySet; 30 import android.view.accessibility.AccessibilityManager; 31 32 import com.android.internal.R; 33 34 import java.util.Collections; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Locale; 38 import java.util.Set; 39 40 public class AccessibilityUtils { 41 public static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':'; 42 43 final static TextUtils.SimpleStringSplitter sStringColonSplitter = 44 new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR); 45 46 /** 47 * @return the set of enabled accessibility services. If there are no services, 48 * it returns the unmodifiable {@link Collections#emptySet()}. 49 */ 50 public static Set<ComponentName> getEnabledServicesFromSettings(Context context) { 51 return getEnabledServicesFromSettings(context, UserHandle.myUserId()); 52 } 53 54 public static boolean hasServiceCrashed(String packageName, String serviceName, 55 List<AccessibilityServiceInfo> enabledServiceInfos) { 56 for (int i = 0; i < enabledServiceInfos.size(); i++) { 57 AccessibilityServiceInfo accessibilityServiceInfo = enabledServiceInfos.get(i); 58 final ServiceInfo serviceInfo = enabledServiceInfos.get(i).getResolveInfo().serviceInfo; 59 if (TextUtils.equals(serviceInfo.packageName, packageName) 60 && TextUtils.equals(serviceInfo.name, serviceName)) { 61 return accessibilityServiceInfo.crashed; 62 } 63 } 64 return false; 65 } 66 67 /** 68 * @return the set of enabled accessibility services for {@param userId}. If there are no 69 * services, it returns the unmodifiable {@link Collections#emptySet()}. 70 */ 71 public static Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) { 72 final String enabledServicesSetting = Settings.Secure.getStringForUser( 73 context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 74 userId); 75 if (enabledServicesSetting == null) { 76 return Collections.emptySet(); 77 } 78 79 final Set<ComponentName> enabledServices = new HashSet<>(); 80 final TextUtils.SimpleStringSplitter colonSplitter = sStringColonSplitter; 81 colonSplitter.setString(enabledServicesSetting); 82 83 while (colonSplitter.hasNext()) { 84 final String componentNameString = colonSplitter.next(); 85 final ComponentName enabledService = ComponentName.unflattenFromString( 86 componentNameString); 87 if (enabledService != null) { 88 enabledServices.add(enabledService); 89 } 90 } 91 92 return enabledServices; 93 } 94 95 /** 96 * @return a localized version of the text resource specified by resId 97 */ 98 public static CharSequence getTextForLocale(Context context, Locale locale, int resId) { 99 final Resources res = context.getResources(); 100 final Configuration config = new Configuration(res.getConfiguration()); 101 config.setLocale(locale); 102 final Context langContext = context.createConfigurationContext(config); 103 return langContext.getText(resId); 104 } 105 106 /** 107 * Changes an accessibility component's state. 108 */ 109 public static void setAccessibilityServiceState(Context context, ComponentName toggledService, 110 boolean enabled) { 111 setAccessibilityServiceState(context, toggledService, enabled, UserHandle.myUserId()); 112 } 113 114 /** 115 * Changes an accessibility component's state for {@param userId}. 116 */ 117 public static void setAccessibilityServiceState(Context context, ComponentName toggledService, 118 boolean enabled, int userId) { 119 // Parse the enabled services. 120 Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings( 121 context, userId); 122 123 if (enabledServices.isEmpty()) { 124 enabledServices = new ArraySet<>(1); 125 } 126 127 // Determine enabled services and accessibility state. 128 boolean accessibilityEnabled = false; 129 if (enabled) { 130 enabledServices.add(toggledService); 131 // Enabling at least one service enables accessibility. 132 accessibilityEnabled = true; 133 } else { 134 enabledServices.remove(toggledService); 135 // Check how many enabled and installed services are present. 136 Set<ComponentName> installedServices = getInstalledServices(context); 137 for (ComponentName enabledService : enabledServices) { 138 if (installedServices.contains(enabledService)) { 139 // Disabling the last service disables accessibility. 140 accessibilityEnabled = true; 141 break; 142 } 143 } 144 } 145 146 // Update the enabled services setting. 147 StringBuilder enabledServicesBuilder = new StringBuilder(); 148 // Keep the enabled services even if they are not installed since we 149 // have no way to know whether the application restore process has 150 // completed. In general the system should be responsible for the 151 // clean up not settings. 152 for (ComponentName enabledService : enabledServices) { 153 enabledServicesBuilder.append(enabledService.flattenToString()); 154 enabledServicesBuilder.append( 155 AccessibilityUtils.ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR); 156 } 157 final int enabledServicesBuilderLength = enabledServicesBuilder.length(); 158 if (enabledServicesBuilderLength > 0) { 159 enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1); 160 } 161 Settings.Secure.putStringForUser(context.getContentResolver(), 162 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 163 enabledServicesBuilder.toString(), userId); 164 } 165 166 /** 167 * Get the name of the service that should be toggled by the accessibility shortcut. Use 168 * an OEM-configurable default if the setting has never been set. 169 * 170 * @param context A valid context 171 * @param userId The user whose settings should be checked 172 * 173 * @return The component name, flattened to a string, of the target service. 174 */ 175 public static String getShortcutTargetServiceComponentNameString( 176 Context context, int userId) { 177 final String currentShortcutServiceId = Settings.Secure.getStringForUser( 178 context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, 179 userId); 180 if (currentShortcutServiceId != null) { 181 return currentShortcutServiceId; 182 } 183 return context.getString(R.string.config_defaultAccessibilityService); 184 } 185 186 /** 187 * Check if the accessibility shortcut is enabled for a user 188 * 189 * @param context A valid context 190 * @param userId The user of interest 191 * @return {@code true} if the shortcut is enabled for the user. {@code false} otherwise. 192 * Note that the shortcut may be enabled, but no action associated with it. 193 */ 194 public static boolean isShortcutEnabled(Context context, int userId) { 195 return Settings.Secure.getIntForUser(context.getContentResolver(), 196 Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, userId) == 1; 197 } 198 199 private static Set<ComponentName> getInstalledServices(Context context) { 200 final Set<ComponentName> installedServices = new HashSet<>(); 201 installedServices.clear(); 202 203 final List<AccessibilityServiceInfo> installedServiceInfos = 204 AccessibilityManager.getInstance(context) 205 .getInstalledAccessibilityServiceList(); 206 if (installedServiceInfos == null) { 207 return installedServices; 208 } 209 210 for (final AccessibilityServiceInfo info : installedServiceInfos) { 211 final ResolveInfo resolveInfo = info.getResolveInfo(); 212 final ComponentName installedService = new ComponentName( 213 resolveInfo.serviceInfo.packageName, 214 resolveInfo.serviceInfo.name); 215 installedServices.add(installedService); 216 } 217 return installedServices; 218 } 219 220 } 221