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