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 /** 44 * @return the set of enabled accessibility services. If there are no services, 45 * it returns the unmodifiable {@link Collections#emptySet()}. 46 */ getEnabledServicesFromSettings(Context context)47 public static Set<ComponentName> getEnabledServicesFromSettings(Context context) { 48 return getEnabledServicesFromSettings(context, UserHandle.myUserId()); 49 } 50 51 /** 52 * Check if the accessibility service is crashed 53 * 54 * @param packageName The package name to check 55 * @param serviceName The service name to check 56 * @param installedServiceInfos The list of installed accessibility service 57 * @return {@code true} if the accessibility service is crashed for the user. 58 * {@code false} otherwise. 59 */ hasServiceCrashed(String packageName, String serviceName, List<AccessibilityServiceInfo> installedServiceInfos)60 public static boolean hasServiceCrashed(String packageName, String serviceName, 61 List<AccessibilityServiceInfo> installedServiceInfos) { 62 for (int i = 0; i < installedServiceInfos.size(); i++) { 63 final AccessibilityServiceInfo accessibilityServiceInfo = installedServiceInfos.get(i); 64 final ServiceInfo serviceInfo = 65 installedServiceInfos.get(i).getResolveInfo().serviceInfo; 66 if (TextUtils.equals(serviceInfo.packageName, packageName) 67 && TextUtils.equals(serviceInfo.name, serviceName)) { 68 return accessibilityServiceInfo.crashed; 69 } 70 } 71 return false; 72 } 73 74 /** 75 * @return the set of enabled accessibility services for {@param userId}. If there are no 76 * services, it returns the unmodifiable {@link Collections#emptySet()}. 77 */ getEnabledServicesFromSettings(Context context, int userId)78 public static Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) { 79 final String enabledServicesSetting = Settings.Secure.getStringForUser( 80 context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 81 userId); 82 if (TextUtils.isEmpty(enabledServicesSetting)) { 83 return Collections.emptySet(); 84 } 85 86 final Set<ComponentName> enabledServices = new HashSet<>(); 87 final TextUtils.StringSplitter colonSplitter = 88 new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR); 89 colonSplitter.setString(enabledServicesSetting); 90 91 for (String componentNameString : colonSplitter) { 92 final ComponentName enabledService = ComponentName.unflattenFromString( 93 componentNameString); 94 if (enabledService != null) { 95 enabledServices.add(enabledService); 96 } 97 } 98 99 return enabledServices; 100 } 101 102 /** 103 * @return a localized version of the text resource specified by resId 104 */ getTextForLocale(Context context, Locale locale, int resId)105 public static CharSequence getTextForLocale(Context context, Locale locale, int resId) { 106 final Resources res = context.getResources(); 107 final Configuration config = new Configuration(res.getConfiguration()); 108 config.setLocale(locale); 109 final Context langContext = context.createConfigurationContext(config); 110 return langContext.getText(resId); 111 } 112 113 /** 114 * Changes an accessibility component's state. 115 */ setAccessibilityServiceState(Context context, ComponentName toggledService, boolean enabled)116 public static void setAccessibilityServiceState(Context context, ComponentName toggledService, 117 boolean enabled) { 118 setAccessibilityServiceState(context, toggledService, enabled, UserHandle.myUserId()); 119 } 120 121 /** 122 * Changes an accessibility component's state for {@param userId}. 123 */ setAccessibilityServiceState(Context context, ComponentName toggledService, boolean enabled, int userId)124 public static void setAccessibilityServiceState(Context context, ComponentName toggledService, 125 boolean enabled, int userId) { 126 // Parse the enabled services. 127 Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings( 128 context, userId); 129 130 if (enabledServices.isEmpty()) { 131 enabledServices = new ArraySet<>(1); 132 } 133 134 // Determine enabled services and accessibility state. 135 boolean accessibilityEnabled = false; 136 if (enabled) { 137 enabledServices.add(toggledService); 138 // Enabling at least one service enables accessibility. 139 accessibilityEnabled = true; 140 } else { 141 enabledServices.remove(toggledService); 142 // Check how many enabled and installed services are present. 143 Set<ComponentName> installedServices = getInstalledServices(context); 144 for (ComponentName enabledService : enabledServices) { 145 if (installedServices.contains(enabledService)) { 146 // Disabling the last service disables accessibility. 147 accessibilityEnabled = true; 148 break; 149 } 150 } 151 } 152 153 // Update the enabled services setting. 154 StringBuilder enabledServicesBuilder = new StringBuilder(); 155 // Keep the enabled services even if they are not installed since we 156 // have no way to know whether the application restore process has 157 // completed. In general the system should be responsible for the 158 // clean up not settings. 159 for (ComponentName enabledService : enabledServices) { 160 enabledServicesBuilder.append(enabledService.flattenToString()); 161 enabledServicesBuilder.append( 162 AccessibilityUtils.ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR); 163 } 164 final int enabledServicesBuilderLength = enabledServicesBuilder.length(); 165 if (enabledServicesBuilderLength > 0) { 166 enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1); 167 } 168 Settings.Secure.putStringForUser(context.getContentResolver(), 169 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 170 enabledServicesBuilder.toString(), userId); 171 } 172 173 /** 174 * Get the name of the service that should be toggled by the accessibility shortcut. Use 175 * an OEM-configurable default if the setting has never been set. 176 * 177 * @param context A valid context 178 * @param userId The user whose settings should be checked 179 * @return The component name, flattened to a string, of the target service. 180 */ getShortcutTargetServiceComponentNameString( Context context, int userId)181 public static String getShortcutTargetServiceComponentNameString( 182 Context context, int userId) { 183 final String currentShortcutServiceId = Settings.Secure.getStringForUser( 184 context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, 185 userId); 186 if (currentShortcutServiceId != null) { 187 return currentShortcutServiceId; 188 } 189 return context.getString(R.string.config_defaultAccessibilityService); 190 } 191 getInstalledServices(Context context)192 private static Set<ComponentName> getInstalledServices(Context context) { 193 final Set<ComponentName> installedServices = new HashSet<>(); 194 installedServices.clear(); 195 196 final List<AccessibilityServiceInfo> installedServiceInfos = 197 AccessibilityManager.getInstance(context) 198 .getInstalledAccessibilityServiceList(); 199 if (installedServiceInfos == null) { 200 return installedServices; 201 } 202 203 for (final AccessibilityServiceInfo info : installedServiceInfos) { 204 final ResolveInfo resolveInfo = info.getResolveInfo(); 205 final ComponentName installedService = new ComponentName( 206 resolveInfo.serviceInfo.packageName, 207 resolveInfo.serviceInfo.name); 208 installedServices.add(installedService); 209 } 210 return installedServices; 211 } 212 213 } 214