diff options
3 files changed, 154 insertions, 61 deletions
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 655c9b32c032..7f44bac8bc6f 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -59,4 +59,8 @@ interface IAccessibilityManager { boolean touchExplorationEnabled); IBinder getWindowToken(int windowId, int userId); + + void enableAccessibilityService(in ComponentName service, int userId); + + void disableAccessibilityService(in ComponentName service, int userId); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index c810e6c89c9f..2763ce383e89 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1812,6 +1812,87 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return mKeyEventDispatcher; } + /** + * Enables accessibility service specified by {@param componentName} for the {@param userId}. + */ + public void enableAccessibilityService(ComponentName componentName, int userId) { + synchronized(mLock) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("only SYSTEM can call enableAccessibilityService."); + } + + SettingsStringHelper settingsHelper = new SettingsStringHelper( + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId); + settingsHelper.addService(componentName); + settingsHelper.writeToSettings(); + + UserState userState = getUserStateLocked(userId); + if (userState.mEnabledServices.add(componentName)) { + onUserStateChangedLocked(userState); + } + } + } + + /** + * Disables accessibility service specified by {@param componentName} for the {@param userId}. + */ + public void disableAccessibilityService(ComponentName componentName, int userId) { + synchronized(mLock) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("only SYSTEM can call disableAccessibility"); + } + + SettingsStringHelper settingsHelper = new SettingsStringHelper( + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId); + settingsHelper.deleteService(componentName); + settingsHelper.writeToSettings(); + + UserState userState = getUserStateLocked(userId); + if (userState.mEnabledServices.remove(componentName)) { + onUserStateChangedLocked(userState); + } + } + } + + private class SettingsStringHelper { + private static final String SETTINGS_DELIMITER = ":"; + private ContentResolver mContentResolver; + private final String mSettingsName; + private Set<String> mServices; + private final int mUserId; + + public SettingsStringHelper(String name, int userId) { + mUserId = userId; + mSettingsName = name; + mContentResolver = mContext.getContentResolver(); + String servicesString = Settings.Secure.getStringForUser( + mContentResolver, mSettingsName, userId); + mServices = new HashSet(); + if (!TextUtils.isEmpty(servicesString)) { + final TextUtils.SimpleStringSplitter colonSplitter = + new TextUtils.SimpleStringSplitter(SETTINGS_DELIMITER.charAt(0)); + colonSplitter.setString(servicesString); + while (colonSplitter.hasNext()) { + final String serviceName = colonSplitter.next(); + mServices.add(serviceName); + } + } + } + + public void addService(ComponentName component) { + mServices.add(component.flattenToString()); + } + + public void deleteService(ComponentName component) { + mServices.remove(component.flattenToString()); + } + + public void writeToSettings() { + Settings.Secure.putStringForUser(mContentResolver, mSettingsName, + TextUtils.join(SETTINGS_DELIMITER, mServices), mUserId); + } + } + @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP); diff --git a/services/core/java/com/android/server/policy/EnableAccessibilityController.java b/services/core/java/com/android/server/policy/EnableAccessibilityController.java index da9c001ea58f..6b203a910a25 100644 --- a/services/core/java/com/android/server/policy/EnableAccessibilityController.java +++ b/services/core/java/com/android/server/policy/EnableAccessibilityController.java @@ -16,7 +16,9 @@ package com.android.server.policy; +import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; +import android.annotation.Nullable; import android.app.ActivityManager; import android.content.ComponentName; import android.content.ContentResolver; @@ -32,19 +34,25 @@ import android.os.ServiceManager; import android.os.UserManager; import android.provider.Settings; import android.speech.tts.TextToSpeech; +import android.util.Log; import android.util.MathUtils; import android.view.IWindowManager; import android.view.MotionEvent; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; +import android.view.WindowManagerInternal; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import com.android.internal.R; +import com.android.server.LocalServices; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class EnableAccessibilityController { + private static final String TAG = "EnableAccessibilityController"; private static final int SPEAK_WARNING_DELAY_MILLIS = 2000; private static final int ENABLE_ACCESSIBILITY_DELAY_MILLIS = 6000; @@ -75,9 +83,6 @@ public class EnableAccessibilityController { } }; - private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface( - ServiceManager.getService("window")); - private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager .Stub.asInterface(ServiceManager.getService("accessibility")); @@ -132,7 +137,7 @@ public class EnableAccessibilityController { && !getInstalledSpeakingAccessibilityServices(context).isEmpty(); } - private static List<AccessibilityServiceInfo> getInstalledSpeakingAccessibilityServices( + public static List<AccessibilityServiceInfo> getInstalledSpeakingAccessibilityServices( Context context) { List<AccessibilityServiceInfo> services = new ArrayList<AccessibilityServiceInfo>(); services.addAll(AccessibilityManager.getInstance(context) @@ -213,71 +218,74 @@ public class EnableAccessibilityController { } private void enableAccessibility() { - List<AccessibilityServiceInfo> services = getInstalledSpeakingAccessibilityServices( - mContext); - if (services.isEmpty()) { - return; + if (enableAccessibility(mContext)) { + mOnAccessibilityEnabledCallback.run(); + } + } + + public static boolean enableAccessibility(Context context) { + final IAccessibilityManager accessibilityManager = IAccessibilityManager + .Stub.asInterface(ServiceManager.getService("accessibility")); + final WindowManagerInternal windowManager = LocalServices.getService( + WindowManagerInternal.class); + final UserManager userManager = (UserManager) context.getSystemService( + Context.USER_SERVICE); + ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context); + if (componentName == null) { + return false; } - boolean keyguardLocked = false; + + boolean keyguardLocked = windowManager.isKeyguardLocked(); + final boolean hasMoreThanOneUser = userManager.getUsers().size() > 1; try { - keyguardLocked = mWindowManager.isKeyguardLocked(); - } catch (RemoteException re) { - /* ignore */ + if (!keyguardLocked || !hasMoreThanOneUser) { + final int userId = ActivityManager.getCurrentUser(); + accessibilityManager.enableAccessibilityService(componentName, userId); + } else if (keyguardLocked) { + accessibilityManager.temporaryEnableAccessibilityStateUntilKeyguardRemoved( + componentName, true /* enableTouchExploration */); + } + } catch (RemoteException e) { + Log.e(TAG, "cannot enable accessibilty: " + e); } - final boolean hasMoreThanOneUser = mUserManager.getUsers().size() > 1; + return true; + } - AccessibilityServiceInfo service = services.get(0); - boolean enableTouchExploration = (service.flags - & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0; - // Try to find a service supporting explore by touch. - if (!enableTouchExploration) { - final int serviceCount = services.size(); - for (int i = 1; i < serviceCount; i++) { - AccessibilityServiceInfo candidate = services.get(i); - if ((candidate.flags & AccessibilityServiceInfo - .FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0) { - enableTouchExploration = true; - service = candidate; - break; - } - } + public static void disableAccessibility(Context context) { + final IAccessibilityManager accessibilityManager = IAccessibilityManager + .Stub.asInterface(ServiceManager.getService("accessibility")); + ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context); + if (componentName == null) { + return; } - ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo; - ComponentName componentName = new ComponentName(serviceInfo.packageName, serviceInfo.name); - if (!keyguardLocked || !hasMoreThanOneUser) { - final int userId = ActivityManager.getCurrentUser(); - String enabledServiceString = componentName.flattenToString(); - ContentResolver resolver = mContext.getContentResolver(); - // Enable one speaking accessibility service. - Settings.Secure.putStringForUser(resolver, - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, - enabledServiceString, userId); - // Allow the services we just enabled to toggle touch exploration. - Settings.Secure.putStringForUser(resolver, - Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, - enabledServiceString, userId); - // Enable touch exploration. - if (enableTouchExploration) { - Settings.Secure.putIntForUser(resolver, Settings.Secure.TOUCH_EXPLORATION_ENABLED, - 1, userId); - } - // Enable accessibility script injection (AndroidVox) for web content. - Settings.Secure.putIntForUser(resolver, Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, - 1, userId); - // Turn on accessibility mode last. - Settings.Secure.putIntForUser(resolver, Settings.Secure.ACCESSIBILITY_ENABLED, - 1, userId); - } else if (keyguardLocked) { - try { - mAccessibilityManager.temporaryEnableAccessibilityStateUntilKeyguardRemoved( - componentName, enableTouchExploration); - } catch (RemoteException re) { - /* ignore */ - } + final int userId = ActivityManager.getCurrentUser(); + try { + accessibilityManager.disableAccessibilityService(componentName, userId); + } catch (RemoteException e) { + Log.e(TAG, "cannot disable accessibility " + e); + } + } + + public static boolean isAccessibilityEnabled(Context context) { + final AccessibilityManager accessibilityManager = + context.getSystemService(AccessibilityManager.class); + List enabledServices = accessibilityManager.getEnabledAccessibilityServiceList( + AccessibilityServiceInfo.FEEDBACK_SPOKEN); + return enabledServices != null && !enabledServices.isEmpty(); + } + + @Nullable + public static ComponentName getInstalledSpeakingAccessibilityServiceComponent( + Context context) { + List<AccessibilityServiceInfo> services = + getInstalledSpeakingAccessibilityServices(context); + if (services.isEmpty()) { + return null; } - mOnAccessibilityEnabledCallback.run(); + ServiceInfo serviceInfo = services.get(0).getResolveInfo().serviceInfo; + return new ComponentName(serviceInfo.packageName, serviceInfo.name); } } |