Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3eca3cd3 authored by Muyuan Li's avatar Muyuan Li Committed by Android (Google) Code Review
Browse files

Merge "Refactor enableAccessibility to AccessibilityManagerService" into nyc-dev

parents 822de0d9 16b8251c
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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);
}
+81 −0
Original line number Diff line number Diff line
@@ -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);
+69 −61
Original line number Diff line number Diff line
@@ -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();
        }
        boolean keyguardLocked = false;
        try {
            keyguardLocked = mWindowManager.isKeyguardLocked();
        } catch (RemoteException re) {
            /* ignore */
    }

        final boolean hasMoreThanOneUser = mUserManager.getUsers().size() > 1;
    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;
        }

        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;
        boolean keyguardLocked = windowManager.isKeyguardLocked();
        final boolean hasMoreThanOneUser = userManager.getUsers().size() > 1;
        try {
            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);
        }

        return true;
    }

    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 */
            accessibilityManager.disableAccessibilityService(componentName, userId);
        } catch (RemoteException e) {
            Log.e(TAG, "cannot disable accessibility " + e);
        }
    }

        mOnAccessibilityEnabledCallback.run();
    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;
        }

        ServiceInfo serviceInfo = services.get(0).getResolveInfo().serviceInfo;
        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
    }
}