FileDocCategorySizeDatePackage
AccessibilityManagerService.javaAPI DocAndroid 5.1 API173708Thu Mar 12 22:22:42 GMT 2015com.android.server.accessibility

AccessibilityManagerService

public class AccessibilityManagerService extends IAccessibilityManager.Stub
This class is instantiated by the system as a system level service and can be accessed only by the system. The task of this service is to be a centralized event dispatch for {@link AccessibilityEvent}s generated across all processes on the device. Events are dispatched to {@link AccessibilityService}s.

Fields Summary
private static final boolean
DEBUG
private static final String
LOG_TAG
private static final int
WAIT_FOR_USER_STATE_FULLY_INITIALIZED_MILLIS
private static final int
WAIT_WINDOWS_TIMEOUT_MILLIS
private static final String
FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE
private static final String
TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED
private static final String
GET_WINDOW_TOKEN
private static final android.content.ComponentName
sFakeAccessibilityServiceComponentName
private static final String
FUNCTION_DUMP
private static final char
COMPONENT_NAME_SEPARATOR
private static final int
OWN_PROCESS_ID
private static final int
MAX_POOL_SIZE
private static final int
WINDOW_ID_UNKNOWN
private static int
sIdCounter
private static int
sNextWindowId
private final android.content.Context
mContext
private final Object
mLock
private final android.util.Pools.Pool
mPendingEventPool
private final android.text.TextUtils.SimpleStringSplitter
mStringColonSplitter
private final List
mEnabledServicesForFeedbackTempList
private final android.graphics.Region
mTempRegion
private final android.graphics.Rect
mTempRect
private final android.graphics.Rect
mTempRect1
private final android.graphics.Point
mTempPoint
private final android.content.pm.PackageManager
mPackageManager
private final android.view.WindowManagerInternal
mWindowManagerService
private final SecurityPolicy
mSecurityPolicy
private final MainHandler
mMainHandler
private InteractionBridge
mInteractionBridge
private android.app.AlertDialog
mEnableTouchExplorationDialog
private AccessibilityInputFilter
mInputFilter
private boolean
mHasInputFilter
private final Set
mTempComponentNameSet
private final List
mTempAccessibilityServiceInfoList
private final android.os.RemoteCallbackList
mGlobalClients
private final android.util.SparseArray
mGlobalInteractionConnections
private final android.util.SparseArray
mGlobalWindowTokens
private final android.util.SparseArray
mUserStates
private final android.os.UserManager
mUserManager
private final com.android.internal.widget.LockPatternUtils
mLockPatternUtils
private int
mCurrentUserId
private boolean
mInitialized
private WindowsForAccessibilityCallback
mWindowsForAccessibilityCallback
Constructors Summary
public AccessibilityManagerService(android.content.Context context)
Creates a new instance.

param
context A {@link Context} instance.

        mContext = context;
        mPackageManager = mContext.getPackageManager();
        mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
        mSecurityPolicy = new SecurityPolicy();
        mMainHandler = new MainHandler(mContext.getMainLooper());
        mLockPatternUtils = new LockPatternUtils(context);
        registerBroadcastReceivers();
        new AccessibilityContentObserver(mMainHandler).register(
                context.getContentResolver());
    
Methods Summary
booleanaccessibilityFocusOnlyInActiveWindow()

        synchronized (mLock) {
            return mWindowsForAccessibilityCallback == null;
        }
    
public intaddAccessibilityInteractionConnection(android.view.IWindow windowToken, android.view.accessibility.IAccessibilityInteractionConnection connection, int userId)

        synchronized (mLock) {
            // We treat calls from a profile as if made by its parent as profiles
            // share the accessibility state of the parent. The call below
            // performs the current profile parent resolution.
            final int resolvedUserId = mSecurityPolicy
                    .resolveCallingUserIdEnforcingPermissionsLocked(userId);
            final int windowId = sNextWindowId++;
            // If the window is from a process that runs across users such as
            // the system UI or the system we add it to the global state that
            // is shared across users.
            if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
                AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(
                        windowId, connection, UserHandle.USER_ALL);
                wrapper.linkToDeath();
                mGlobalInteractionConnections.put(windowId, wrapper);
                mGlobalWindowTokens.put(windowId, windowToken.asBinder());
                if (DEBUG) {
                    Slog.i(LOG_TAG, "Added global connection for pid:" + Binder.getCallingPid()
                            + " with windowId: " + windowId + " and  token: " + windowToken.asBinder());
                }
            } else {
                AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(
                        windowId, connection, resolvedUserId);
                wrapper.linkToDeath();
                UserState userState = getUserStateLocked(resolvedUserId);
                userState.mInteractionConnections.put(windowId, wrapper);
                userState.mWindowTokens.put(windowId, windowToken.asBinder());
                if (DEBUG) {
                    Slog.i(LOG_TAG, "Added user connection for pid:" + Binder.getCallingPid()
                            + " with windowId: " + windowId + " and userId:" + mCurrentUserId
                            + " and  token: " + windowToken.asBinder());
                }
            }
            return windowId;
        }
    
public intaddClient(android.view.accessibility.IAccessibilityManagerClient client, int userId)

        synchronized (mLock) {
            // We treat calls from a profile as if made by its parent as profiles
            // share the accessibility state of the parent. The call below
            // performs the current profile parent resolution.
            final int resolvedUserId = mSecurityPolicy
                    .resolveCallingUserIdEnforcingPermissionsLocked(userId);
            // If the client is from a process that runs across users such as
            // the system UI or the system we add it to the global state that
            // is shared across users.
            UserState userState = getUserStateLocked(resolvedUserId);
            if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
                mGlobalClients.register(client);
                if (DEBUG) {
                    Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
                }
                return userState.getClientState();
            } else {
                userState.mClients.register(client);
                // If this client is not for the current user we do not
                // return a state since it is not for the foreground user.
                // We will send the state to the client on a user switch.
                if (DEBUG) {
                    Slog.i(LOG_TAG, "Added user client for pid:" + Binder.getCallingPid()
                            + " and userId:" + mCurrentUserId);
                }
                return (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0;
            }
        }
    
private voidaddServiceLocked(com.android.server.accessibility.AccessibilityManagerService$Service service, com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        try {
            service.onAdded();
            userState.mBoundServices.add(service);
            userState.mComponentNameToServiceMap.put(service.mComponentName, service);
        } catch (RemoteException re) {
            /* do nothing */
        }
    
private booleancanDispatchEventToServiceLocked(com.android.server.accessibility.AccessibilityManagerService$Service service, android.view.accessibility.AccessibilityEvent event, int handledFeedbackTypes)
Determines if given event can be dispatched to a service based on the package of the event source and already notified services for that event type. Specifically, a service is notified if it is interested in events from the package and no other service providing the same feedback type has been notified. Exception are services the provide generic feedback (feedback type left as a safety net for unforeseen feedback types) which are always notified.

param
service The potential receiver.
param
event The event.
param
handledFeedbackTypes The feedback types for which services have been notified.
return
True if the listener should be notified, false otherwise.


        if (!service.canReceiveEventsLocked()) {
            return false;
        }

        if (event.getWindowId() != WINDOW_ID_UNKNOWN && !event.isImportantForAccessibility()
                && (service.mFetchFlags
                        & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) == 0) {
            return false;
        }

        int eventType = event.getEventType();
        if ((service.mEventTypes & eventType) != eventType) {
            return false;
        }

        Set<String> packageNames = service.mPackageNames;
        String packageName = (event.getPackageName() != null)
                ? event.getPackageName().toString() : null;

        if (packageNames.isEmpty() || packageNames.contains(packageName)) {
            int feedbackType = service.mFeedbackType;
            if ((handledFeedbackTypes & feedbackType) != feedbackType
                    || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
                return true;
            }
        }

        return false;
    
private booleancanRequestAndRequestsEnhancedWebAccessibilityLocked(com.android.server.accessibility.AccessibilityManagerService$Service service)

        if (!service.canReceiveEventsLocked() || !service.mRequestEnhancedWebAccessibility ) {
            return false;
        }
        if (service.mIsAutomation || (service.mAccessibilityServiceInfo.getCapabilities()
               & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY) != 0) {
            return true;
        }
        return false;
    
private booleancanRequestAndRequestsTouchExplorationLocked(com.android.server.accessibility.AccessibilityManagerService$Service service)

        // Service not ready or cannot request the feature - well nothing to do.
        if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) {
            return false;
        }
        // UI test automation service can always enable it.
        if (service.mIsAutomation) {
            return true;
        }
        if (service.mResolveInfo.serviceInfo.applicationInfo.targetSdkVersion
                <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            // Up to JB-MR1 we had a white list with services that can enable touch
            // exploration. When a service is first started we show a dialog to the
            // use to get a permission to white list the service.
            UserState userState = getUserStateLocked(service.mUserId);
            if (userState.mTouchExplorationGrantedServices.contains(service.mComponentName)) {
                return true;
            } else if (mEnableTouchExplorationDialog == null
                    || !mEnableTouchExplorationDialog.isShowing()) {
                mMainHandler.obtainMessage(
                        MainHandler.MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG,
                        service).sendToTarget();
            }
        } else {
            // Starting in JB-MR2 we request an accessibility service to declare
            // certain capabilities in its meta-data to allow it to enable the
            // corresponding features.
            if ((service.mAccessibilityServiceInfo.getCapabilities()
                    & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION) != 0) {
                return true;
            }
        }
        return false;
    
public voiddump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)

        mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
        synchronized (mLock) {
            pw.println("ACCESSIBILITY MANAGER (dumpsys accessibility)");
            pw.println();
            final int userCount = mUserStates.size();
            for (int i = 0; i < userCount; i++) {
                UserState userState = mUserStates.valueAt(i);
                pw.append("User state[attributes:{id=" + userState.mUserId);
                pw.append(", currentUser=" + (userState.mUserId == mCurrentUserId));
                pw.append(", accessibilityEnabled=" + userState.mIsAccessibilityEnabled);
                pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled);
                pw.append(", displayMagnificationEnabled="
                        + userState.mIsDisplayMagnificationEnabled);
                if (userState.mUiAutomationService != null) {
                    pw.append(", ");
                    userState.mUiAutomationService.dump(fd, pw, args);
                    pw.println();
                }
                pw.append("}");
                pw.println();
                pw.append("           services:{");
                final int serviceCount = userState.mBoundServices.size();
                for (int j = 0; j < serviceCount; j++) {
                    if (j > 0) {
                        pw.append(", ");
                        pw.println();
                        pw.append("                     ");
                    }
                    Service service = userState.mBoundServices.get(j);
                    service.dump(fd, pw, args);
                }
                pw.println("}]");
                pw.println();
            }
            if (mSecurityPolicy.mWindows != null) {
                final int windowCount = mSecurityPolicy.mWindows.size();
                for (int j = 0; j < windowCount; j++) {
                    if (j > 0) {
                        pw.append(',");
                        pw.println();
                    }
                    pw.append("Window[");
                    AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(j);
                    pw.append(window.toString());
                    pw.append(']");
                }
            }
        }
    
private voidensureWindowsAvailableTimed()

        synchronized (mLock) {
            if (mSecurityPolicy.mWindows != null) {
                return;
            }
            // If we have no registered callback, update the state we
            // we may have to register one but it didn't happen yet.
            if (mWindowsForAccessibilityCallback == null) {
                UserState userState = getCurrentUserStateLocked();
                onUserStateChangedLocked(userState);
            }
            // We have no windows but do not care about them, done.
            if (mWindowsForAccessibilityCallback == null) {
                return;
            }

            // Wait for the windows with a timeout.
            final long startMillis = SystemClock.uptimeMillis();
            while (mSecurityPolicy.mWindows == null) {
                final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
                final long remainMillis = WAIT_WINDOWS_TIMEOUT_MILLIS - elapsedMillis;
                if (remainMillis <= 0) {
                    return;
                }
                try {
                    mLock.wait(remainMillis);
                } catch (InterruptedException ie) {
                    /* ignore */
                }
            }
        }
    
private intfindWindowIdLocked(android.os.IBinder token)

        final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
        if (globalIndex >= 0) {
            return mGlobalWindowTokens.keyAt(globalIndex);
        }
        UserState userState = getCurrentUserStateLocked();
        final int userIndex = userState.mWindowTokens.indexOfValue(token);
        if (userIndex >= 0) {
            return userState.mWindowTokens.keyAt(userIndex);
        }
        return -1;
    
booleangetAccessibilityFocusClickPointInScreen(android.graphics.Point outPoint)
Gets a point within the accessibility focused node where we can send down and up events to perform a click.

param
outPoint The click point to populate.
return
Whether accessibility a click point was found and set.

        return getInteractionBridgeLocked()
                .getAccessibilityFocusClickPointInScreenNotLocked(outPoint);
    
intgetActiveWindowId()

        return mSecurityPolicy.getActiveWindowId();
    
private android.view.MagnificationSpecgetCompatibleMagnificationSpecLocked(int windowId)

        IBinder windowToken = mGlobalWindowTokens.get(windowId);
        if (windowToken == null) {
            windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
        }
        if (windowToken != null) {
            return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
                    windowToken);
        }
        return null;
    
private com.android.server.accessibility.AccessibilityManagerService$UserStategetCurrentUserStateLocked()


       
        return getUserStateLocked(mCurrentUserId);
    
public java.util.ListgetEnabledAccessibilityServiceList(int feedbackType, int userId)

        List<AccessibilityServiceInfo> result = null;
        synchronized (mLock) {
            // We treat calls from a profile as if made by its parent as profiles
            // share the accessibility state of the parent. The call below
            // performs the current profile parent resolution.
            final int resolvedUserId = mSecurityPolicy
                    .resolveCallingUserIdEnforcingPermissionsLocked(userId);

            // The automation service is a fake one and should not be reported
            // to clients as being enabled. The automation service is always the
            // only active one, if it exists.
            UserState userState = getUserStateLocked(resolvedUserId);
            if (userState.mUiAutomationService != null) {
                return Collections.emptyList();
            }

            result = mEnabledServicesForFeedbackTempList;
            result.clear();
            List<Service> services = userState.mBoundServices;
            while (feedbackType != 0) {
                final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType));
                feedbackType &= ~feedbackTypeBit;
                final int serviceCount = services.size();
                for (int i = 0; i < serviceCount; i++) {
                    Service service = services.get(i);
                    if ((service.mFeedbackType & feedbackTypeBit) != 0) {
                        result.add(service.mAccessibilityServiceInfo);
                    }
                }
            }
        }
        return result;
    
public java.util.ListgetInstalledAccessibilityServiceList(int userId)

        synchronized (mLock) {
            // We treat calls from a profile as if made by its parent as profiles
            // share the accessibility state of the parent. The call below
            // performs the current profile parent resolution.
            final int resolvedUserId = mSecurityPolicy
                    .resolveCallingUserIdEnforcingPermissionsLocked(userId);
            // The automation service is a fake one and should not be reported
            // to clients as being installed - it really is not.
            UserState userState = getUserStateLocked(resolvedUserId);
            if (userState.mUiAutomationService != null) {
                List<AccessibilityServiceInfo> installedServices = new ArrayList<>();
                installedServices.addAll(userState.mInstalledServices);
                installedServices.remove(userState.mUiAutomationService.mAccessibilityServiceInfo);
                return installedServices;
            }
            return userState.mInstalledServices;
        }
    
private com.android.server.accessibility.AccessibilityManagerService$InteractionBridgegetInteractionBridgeLocked()

        if (mInteractionBridge == null) {
            mInteractionBridge = new InteractionBridge();
        }
        return mInteractionBridge;
    
private com.android.server.accessibility.AccessibilityManagerService$UserStategetUserStateLocked(int userId)

        UserState state = mUserStates.get(userId);
        if (state == null) {
            state = new UserState(userId);
            mUserStates.put(userId, state);
        }
        return state;
    
booleangetWindowBounds(int windowId, android.graphics.Rect outBounds)
Gets the bounds of a window.

param
outBounds The output to which to write the bounds.

        IBinder token;
        synchronized (mLock) {
            token = mGlobalWindowTokens.get(windowId);
            if (token == null) {
                token = getCurrentUserStateLocked().mWindowTokens.get(windowId);
            }
        }
        mWindowManagerService.getWindowFrame(token, outBounds);
        if (!outBounds.isEmpty()) {
            return true;
        }
        return false;
    
public android.os.IBindergetWindowToken(int windowId)

        mSecurityPolicy.enforceCallingPermission(
                Manifest.permission.RETRIEVE_WINDOW_TOKEN,
                GET_WINDOW_TOKEN);
        synchronized (mLock) {
            // We treat calls from a profile as if made by its parent as profiles
            // share the accessibility state of the parent. The call below
            // performs the current profile parent resolution.
            final int resolvedUserId = mSecurityPolicy
                    .resolveCallingUserIdEnforcingPermissionsLocked(
                            UserHandle.getCallingUserId());
            if (resolvedUserId != mCurrentUserId) {
                return null;
            }
            if (mSecurityPolicy.findWindowById(windowId) == null) {
                return null;
            }
            IBinder token = mGlobalWindowTokens.get(windowId);
            if (token != null) {
                return token;
            }
            return getCurrentUserStateLocked().mWindowTokens.get(windowId);
        }
    
private booleanhasRunningServicesLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        return !userState.mBoundServices.isEmpty() || !userState.mBindingServices.isEmpty();
    
public voidinterrupt(int userId)

        CopyOnWriteArrayList<Service> services;
        synchronized (mLock) {
            // We treat calls from a profile as if made by its parent as profiles
            // share the accessibility state of the parent. The call below
            // performs the current profile parent resolution.
            final int resolvedUserId = mSecurityPolicy
                    .resolveCallingUserIdEnforcingPermissionsLocked(userId);
            // This method does nothing for a background user.
            if (resolvedUserId != mCurrentUserId) {
                return;
            }
            services = getUserStateLocked(resolvedUserId).mBoundServices;
        }
        for (int i = 0, count = services.size(); i < count; i++) {
            Service service = services.get(i);
            try {
                service.mServiceInterface.onInterrupt();
            } catch (RemoteException re) {
                Slog.e(LOG_TAG, "Error during sending interrupt request to "
                    + service.mService, re);
            }
        }
    
private voidmanageServicesLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        Map<ComponentName, Service> componentNameToServiceMap =
                userState.mComponentNameToServiceMap;
        boolean isEnabled = userState.mIsAccessibilityEnabled;

        for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) {
            AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i);
            ComponentName componentName = ComponentName.unflattenFromString(
                    installedService.getId());
            Service service = componentNameToServiceMap.get(componentName);

            if (isEnabled) {
                // Wait for the binding if it is in process.
                if (userState.mBindingServices.contains(componentName)) {
                    continue;
                }
                if (userState.mEnabledServices.contains(componentName)) {
                    if (service == null) {
                        service = new Service(userState.mUserId, componentName, installedService);
                    } else if (userState.mBoundServices.contains(service)) {
                        continue;
                    }
                    service.bindLocked();
                } else {
                    if (service != null) {
                        service.unbindLocked();
                    }
                }
            } else {
                if (service != null) {
                    service.unbindLocked();
                } else {
                    userState.mBindingServices.remove(componentName);
                }
            }
        }

        // No enabled installed services => disable accessibility to avoid
        // sending accessibility events with no recipient across processes.
        if (isEnabled && userState.mBoundServices.isEmpty()
                && userState.mBindingServices.isEmpty()) {
            userState.mIsAccessibilityEnabled = false;
            Settings.Secure.putIntForUser(mContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_ENABLED, 0, userState.mUserId);
        }
    
private voidnotifyAccessibilityServicesDelayedLocked(android.view.accessibility.AccessibilityEvent event, boolean isDefault)
Performs {@link AccessibilityService}s delayed notification. The delay is configurable and denotes the period after the last event before notifying the service.

param
event The event.
param
isDefault True to notify default listeners, not default services.

        try {
            UserState state = getCurrentUserStateLocked();
            for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
                Service service = state.mBoundServices.get(i);

                if (service.mIsDefault == isDefault) {
                    if (canDispatchEventToServiceLocked(service, event,
                            state.mHandledFeedbackTypes)) {
                        state.mHandledFeedbackTypes |= service.mFeedbackType;
                        service.notifyAccessibilityEvent(event);
                    }
                }
            }
        } catch (IndexOutOfBoundsException oobe) {
            // An out of bounds exception can happen if services are going away
            // as the for loop is running. If that happens, just bail because
            // there are no more services to notify.
        }
    
private voidnotifyClearAccessibilityCacheLocked()

        UserState state = getCurrentUserStateLocked();
        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
            Service service = state.mBoundServices.get(i);
            service.notifyClearAccessibilityNodeInfoCache();
        }
    
private booleannotifyGestureLocked(int gestureId, boolean isDefault)

        // TODO: Now we are giving the gestures to the last enabled
        //       service that can handle them which is the last one
        //       in our list since we write the last enabled as the
        //       last record in the enabled services setting. Ideally,
        //       the user should make the call which service handles
        //       gestures. However, only one service should handle
        //       gestures to avoid user frustration when different
        //       behavior is observed from different combinations of
        //       enabled accessibility services.
        UserState state = getCurrentUserStateLocked();
        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
            Service service = state.mBoundServices.get(i);
            if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
                service.notifyGesture(gestureId);
                return true;
            }
        }
        return false;
    
booleannotifyKeyEvent(android.view.KeyEvent event, int policyFlags)

        synchronized (mLock) {
            KeyEvent localClone = KeyEvent.obtain(event);
            boolean handled = notifyKeyEventLocked(localClone, policyFlags, false);
            if (!handled) {
                handled = notifyKeyEventLocked(localClone, policyFlags, true);
            }
            return handled;
        }
    
private booleannotifyKeyEventLocked(android.view.KeyEvent event, int policyFlags, boolean isDefault)

        // TODO: Now we are giving the key events to the last enabled
        //       service that can handle them Ideally, the user should
        //       make the call which service handles key events. However,
        //       only one service should handle key events to avoid user
        //       frustration when different behavior is observed from
        //       different combinations of enabled accessibility services.
        UserState state = getCurrentUserStateLocked();
        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
            Service service = state.mBoundServices.get(i);
            // Key events are handled only by services that declared
            // this capability and requested to filter key events.
            if (!service.mRequestFilterKeyEvents ||
                    (service.mAccessibilityServiceInfo.getCapabilities() & AccessibilityServiceInfo
                            .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) == 0) {
                continue;
            }
            if (service.mIsDefault == isDefault) {
                service.notifyKeyEvent(event, policyFlags);
                return true;
            }
        }
        return false;
    
private com.android.server.accessibility.AccessibilityManagerService$PendingEventobtainPendingEventLocked(android.view.KeyEvent event, int policyFlags, int sequence)

        PendingEvent pendingEvent = mPendingEventPool.acquire();
        if (pendingEvent == null) {
            pendingEvent = new PendingEvent();
        }
        pendingEvent.event = event;
        pendingEvent.policyFlags = policyFlags;
        pendingEvent.sequence = sequence;
        return pendingEvent;
    
booleanonGesture(int gestureId)

        synchronized (mLock) {
            boolean handled = notifyGestureLocked(gestureId, false);
            if (!handled) {
                handled = notifyGestureLocked(gestureId, true);
            }
            return handled;
        }
    
voidonMagnificationStateChanged()

        notifyClearAccessibilityCacheLocked();
    
voidonTouchInteractionEnd()

        mSecurityPolicy.onTouchInteractionEnd();
    
voidonTouchInteractionStart()

        mSecurityPolicy.onTouchInteractionStart();
    
private voidonUserStateChangedLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        // TODO: Remove this hack
        mInitialized = true;
        updateLegacyCapabilitiesLocked(userState);
        updateServicesLocked(userState);
        updateWindowsForAccessibilityCallbackLocked(userState);
        updateAccessibilityFocusBehaviorLocked(userState);
        updateFilterKeyEventsLocked(userState);
        updateTouchExplorationLocked(userState);
        updateEnhancedWebAccessibilityLocked(userState);
        updateDisplayColorAdjustmentSettingsLocked(userState);
        scheduleUpdateInputFilter(userState);
        scheduleUpdateClientsIfNeededLocked(userState);
    
private voidpersistComponentNamesToSettingLocked(java.lang.String settingName, java.util.Set componentNames, int userId)
Persists the component names in the specified setting in a colon separated fashion.

param
settingName The setting name.
param
componentNames The component names.

        StringBuilder builder = new StringBuilder();
        for (ComponentName componentName : componentNames) {
            if (builder.length() > 0) {
                builder.append(COMPONENT_NAME_SEPARATOR);
            }
            builder.append(componentName.flattenToShortString());
        }
        Settings.Secure.putStringForUser(mContext.getContentResolver(),
                settingName, builder.toString(), userId);
    
private booleanreadAccessibilityEnabledSettingLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        final boolean accessibilityEnabled = Settings.Secure.getIntForUser(
               mContext.getContentResolver(),
               Settings.Secure.ACCESSIBILITY_ENABLED, 0, userState.mUserId) == 1;
        if (accessibilityEnabled != userState.mIsAccessibilityEnabled) {
            userState.mIsAccessibilityEnabled = accessibilityEnabled;
            return true;
        }
        return false;
    
private voidreadComponentNamesFromSettingLocked(java.lang.String settingName, int userId, java.util.Set outComponentNames)
Populates a set with the {@link ComponentName}s stored in a colon separated value setting for a given user.

param
settingName The setting to parse.
param
userId The user id.
param
outComponentNames The output component names.

        String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                settingName, userId);
        outComponentNames.clear();
        if (settingValue != null) {
            TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
            splitter.setString(settingValue);
            while (splitter.hasNext()) {
                String str = splitter.next();
                if (str == null || str.length() <= 0) {
                    continue;
                }
                ComponentName enabledService = ComponentName.unflattenFromString(str);
                if (enabledService != null) {
                    outComponentNames.add(enabledService);
                }
            }
        }
    
private booleanreadConfigurationForUserStateLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        boolean somthingChanged = readAccessibilityEnabledSettingLocked(userState);
        somthingChanged |= readInstalledAccessibilityServiceLocked(userState);
        somthingChanged |= readEnabledAccessibilityServicesLocked(userState);
        somthingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState);
        somthingChanged |= readTouchExplorationEnabledSettingLocked(userState);
        somthingChanged |= readHighTextContrastEnabledSettingLocked(userState);
        somthingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
        somthingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
        somthingChanged |= readDisplayColorAdjustmentSettingsLocked(userState);
        return somthingChanged;
    
private booleanreadDisplayColorAdjustmentSettingsLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        final boolean displayAdjustmentsEnabled = DisplayAdjustmentUtils.hasAdjustments(mContext,
                userState.mUserId);
        if (displayAdjustmentsEnabled != userState.mHasDisplayColorAdjustment) {
            userState.mHasDisplayColorAdjustment = displayAdjustmentsEnabled;
            return true;
        }
        // If display adjustment is enabled, always assume there was a change in
        // the adjustment settings.
        return displayAdjustmentsEnabled;
    
private booleanreadDisplayMagnificationEnabledSettingLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser(
                mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
                0, userState.mUserId) == 1;
        if (displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled) {
            userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled;
            return true;
        }
        return false;
    
private booleanreadEnabledAccessibilityServicesLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        mTempComponentNameSet.clear();
        readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                userState.mUserId, mTempComponentNameSet);
        if (!mTempComponentNameSet.equals(userState.mEnabledServices)) {
            userState.mEnabledServices.clear();
            userState.mEnabledServices.addAll(mTempComponentNameSet);
            mTempComponentNameSet.clear();
            return true;
        }
        mTempComponentNameSet.clear();
        return false;
    
private booleanreadEnhancedWebAccessibilityEnabledChangedLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

         final boolean enhancedWeAccessibilityEnabled = Settings.Secure.getIntForUser(
                mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION,
                0, userState.mUserId) == 1;
         if (enhancedWeAccessibilityEnabled != userState.mIsEnhancedWebAccessibilityEnabled) {
             userState.mIsEnhancedWebAccessibilityEnabled = enhancedWeAccessibilityEnabled;
             return true;
         }
         return false;
    
private booleanreadHighTextContrastEnabledSettingLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        final boolean highTextContrastEnabled = Settings.Secure.getIntForUser(
                mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0,
                userState.mUserId) == 1;
        if (highTextContrastEnabled != userState.mIsTextHighContrastEnabled) {
            userState.mIsTextHighContrastEnabled = highTextContrastEnabled;
            return true;
        }
        return false;
    
private booleanreadInstalledAccessibilityServiceLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        mTempAccessibilityServiceInfoList.clear();

        List<ResolveInfo> installedServices = mPackageManager.queryIntentServicesAsUser(
                new Intent(AccessibilityService.SERVICE_INTERFACE),
                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
                mCurrentUserId);

        for (int i = 0, count = installedServices.size(); i < count; i++) {
            ResolveInfo resolveInfo = installedServices.get(i);
            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
            if (!android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
                    serviceInfo.permission)) {
                Slog.w(LOG_TAG, "Skipping accessibilty service " + new ComponentName(
                        serviceInfo.packageName, serviceInfo.name).flattenToShortString()
                        + ": it does not require the permission "
                        + android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE);
                continue;
            }
            AccessibilityServiceInfo accessibilityServiceInfo;
            try {
                accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
                mTempAccessibilityServiceInfoList.add(accessibilityServiceInfo);
            } catch (XmlPullParserException | IOException xppe) {
                Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe);
            }
        }

        if (!mTempAccessibilityServiceInfoList.equals(userState.mInstalledServices)) {
            userState.mInstalledServices.clear();
            userState.mInstalledServices.addAll(mTempAccessibilityServiceInfoList);
            mTempAccessibilityServiceInfoList.clear();
            return true;
        }

        mTempAccessibilityServiceInfoList.clear();
        return false;
    
private booleanreadTouchExplorationEnabledSettingLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        final boolean touchExplorationEnabled = Settings.Secure.getIntForUser(
                mContext.getContentResolver(),
                Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId) == 1;
        if (touchExplorationEnabled != userState.mIsTouchExplorationEnabled) {
            userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
            return true;
        }
        return false;
    
private booleanreadTouchExplorationGrantedAccessibilityServicesLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        mTempComponentNameSet.clear();
        readComponentNamesFromSettingLocked(
                Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
                userState.mUserId, mTempComponentNameSet);
        if (!mTempComponentNameSet.equals(userState.mTouchExplorationGrantedServices)) {
            userState.mTouchExplorationGrantedServices.clear();
            userState.mTouchExplorationGrantedServices.addAll(mTempComponentNameSet);
            mTempComponentNameSet.clear();
            return true;
        }
        mTempComponentNameSet.clear();
        return false;
    
private voidrecyclePendingEventLocked(com.android.server.accessibility.AccessibilityManagerService$PendingEvent pendingEvent)

        pendingEvent.clear();
        mPendingEventPool.release(pendingEvent);
    
private voidregisterBroadcastReceivers()

        PackageMonitor monitor = new PackageMonitor() {
            @Override
            public void onSomePackagesChanged() {
                synchronized (mLock) {
                    // Only the profile parent can install accessibility services.
                    // Therefore we ignore packages from linked profiles.
                    if (getChangingUserId() != mCurrentUserId) {
                        return;
                    }
                    // We will update when the automation service dies.
                    UserState userState = getCurrentUserStateLocked();
                    // We have to reload the installed services since some services may
                    // have different attributes, resolve info (does not support equals),
                    // etc. Remove them then to force reload. Do it even if automation is
                    // running since when it goes away, we will have to reload as well.
                    userState.mInstalledServices.clear();
                    if (userState.mUiAutomationService == null) {
                        if (readConfigurationForUserStateLocked(userState)) {
                            onUserStateChangedLocked(userState);
                        }
                    }
                }
            }

            @Override
            public void onPackageRemoved(String packageName, int uid) {
                synchronized (mLock) {
                    final int userId = getChangingUserId();
                    // Only the profile parent can install accessibility services.
                    // Therefore we ignore packages from linked profiles.
                    if (userId != mCurrentUserId) {
                        return;
                    }
                    UserState userState = getUserStateLocked(userId);
                    Iterator<ComponentName> it = userState.mEnabledServices.iterator();
                    while (it.hasNext()) {
                        ComponentName comp = it.next();
                        String compPkg = comp.getPackageName();
                        if (compPkg.equals(packageName)) {
                            it.remove();
                            // Update the enabled services setting.
                            persistComponentNamesToSettingLocked(
                                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                                    userState.mEnabledServices, userId);
                            // Update the touch exploration granted services setting.
                            userState.mTouchExplorationGrantedServices.remove(comp);
                            persistComponentNamesToSettingLocked(
                                    Settings.Secure.
                                    TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
                                    userState.mTouchExplorationGrantedServices, userId);
                            // We will update when the automation service dies.
                            if (userState.mUiAutomationService == null) {
                                onUserStateChangedLocked(userState);
                            }
                            return;
                        }
                    }
                }
            }

            @Override
            public boolean onHandleForceStop(Intent intent, String[] packages,
                    int uid, boolean doit) {
                synchronized (mLock) {
                    final int userId = getChangingUserId();
                    // Only the profile parent can install accessibility services.
                    // Therefore we ignore packages from linked profiles.
                    if (userId != mCurrentUserId) {
                        return false;
                    }
                    UserState userState = getUserStateLocked(userId);
                    Iterator<ComponentName> it = userState.mEnabledServices.iterator();
                    while (it.hasNext()) {
                        ComponentName comp = it.next();
                        String compPkg = comp.getPackageName();
                        for (String pkg : packages) {
                            if (compPkg.equals(pkg)) {
                                if (!doit) {
                                    return true;
                                }
                                it.remove();
                                persistComponentNamesToSettingLocked(
                                        Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                                        userState.mEnabledServices, userId);
                                // We will update when the automation service dies.
                                if (userState.mUiAutomationService == null) {
                                    onUserStateChangedLocked(userState);
                                }
                            }
                        }
                    }
                    return false;
                }
            }
        };

        // package changes
        monitor.register(mContext, null,  UserHandle.ALL, true);

        // user change and unlock
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
        intentFilter.addAction(Intent.ACTION_USER_PRESENT);

        mContext.registerReceiverAsUser(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
                } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                    // We will update when the automation service dies.
                    UserState userState = getCurrentUserStateLocked();
                    if (userState.mUiAutomationService == null) {
                        if (readConfigurationForUserStateLocked(userState)) {
                            onUserStateChangedLocked(userState);
                        }
                    }
                }
            }
        }, UserHandle.ALL, intentFilter, null, null);
    
public voidregisterUiTestAutomationService(android.os.IBinder owner, android.accessibilityservice.IAccessibilityServiceClient serviceClient, android.accessibilityservice.AccessibilityServiceInfo accessibilityServiceInfo)

        mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
                FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE);

        accessibilityServiceInfo.setComponentName(sFakeAccessibilityServiceComponentName);

        synchronized (mLock) {
            UserState userState = getCurrentUserStateLocked();

            if (userState.mUiAutomationService != null) {
                throw new IllegalStateException("UiAutomationService " + serviceClient
                        + "already registered!");
            }

            try {
                owner.linkToDeath(userState.mUiAutomationSerivceOnwerDeathRecipient, 0);
            } catch (RemoteException re) {
                Slog.e(LOG_TAG, "Couldn't register for the death of a"
                        + " UiTestAutomationService!", re);
                return;
            }

            userState.mUiAutomationServiceOwner = owner;
            userState.mUiAutomationServiceClient = serviceClient;

            // Set the temporary state.
            userState.mIsAccessibilityEnabled = true;
            userState.mIsTouchExplorationEnabled = false;
            userState.mIsEnhancedWebAccessibilityEnabled = false;
            userState.mIsDisplayMagnificationEnabled = false;
            userState.mInstalledServices.add(accessibilityServiceInfo);
            userState.mEnabledServices.clear();
            userState.mEnabledServices.add(sFakeAccessibilityServiceComponentName);
            userState.mTouchExplorationGrantedServices.add(sFakeAccessibilityServiceComponentName);

            // Use the new state instead of settings.
            onUserStateChangedLocked(userState);
        }
    
public voidremoveAccessibilityInteractionConnection(android.view.IWindow window)

        synchronized (mLock) {
            // We treat calls from a profile as if made by its parent as profiles
            // share the accessibility state of the parent. The call below
            // performs the current profile parent resolution.
            mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
                    UserHandle.getCallingUserId());
            IBinder token = window.asBinder();
            final int removedWindowId = removeAccessibilityInteractionConnectionInternalLocked(
                    token, mGlobalWindowTokens, mGlobalInteractionConnections);
            if (removedWindowId >= 0) {
                if (DEBUG) {
                    Slog.i(LOG_TAG, "Removed global connection for pid:" + Binder.getCallingPid()
                            + " with windowId: " + removedWindowId + " and token: " + window.asBinder());
                }
                return;
            }
            final int userCount = mUserStates.size();
            for (int i = 0; i < userCount; i++) {
                UserState userState = mUserStates.valueAt(i);
                final int removedWindowIdForUser =
                        removeAccessibilityInteractionConnectionInternalLocked(
                        token, userState.mWindowTokens, userState.mInteractionConnections);
                if (removedWindowIdForUser >= 0) {
                    if (DEBUG) {
                        Slog.i(LOG_TAG, "Removed user connection for pid:" + Binder.getCallingPid()
                                + " with windowId: " + removedWindowIdForUser + " and userId:"
                                + mUserStates.keyAt(i) + " and token: " + window.asBinder());
                    }
                    return;
                }
            }
        }
    
private intremoveAccessibilityInteractionConnectionInternalLocked(android.os.IBinder windowToken, android.util.SparseArray windowTokens, android.util.SparseArray interactionConnections)

        final int count = windowTokens.size();
        for (int i = 0; i < count; i++) {
            if (windowTokens.valueAt(i) == windowToken) {
                final int windowId = windowTokens.keyAt(i);
                windowTokens.removeAt(i);
                AccessibilityConnectionWrapper wrapper = interactionConnections.get(windowId);
                wrapper.unlinkToDeath();
                interactionConnections.remove(windowId);
                return windowId;
            }
        }
        return -1;
    
private voidremoveAccessibilityInteractionConnectionLocked(int windowId, int userId)
Removes an AccessibilityInteractionConnection.

param
windowId The id of the window to which the connection is targeted.
param
userId The id of the user owning the connection. UserHandle.USER_ALL if global.

        if (userId == UserHandle.USER_ALL) {
            mGlobalWindowTokens.remove(windowId);
            mGlobalInteractionConnections.remove(windowId);
        } else {
            UserState userState = getCurrentUserStateLocked();
            userState.mWindowTokens.remove(windowId);
            userState.mInteractionConnections.remove(windowId);
        }
        if (DEBUG) {
            Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
        }
    
private voidremoveServiceLocked(com.android.server.accessibility.AccessibilityManagerService$Service service, com.android.server.accessibility.AccessibilityManagerService$UserState userState)
Removes a service.

param
service The service.

        userState.mBoundServices.remove(service);
        userState.mComponentNameToServiceMap.remove(service.mComponentName);
        service.onRemoved();
    
private voidremoveUser(int userId)

        synchronized (mLock) {
            mUserStates.remove(userId);
        }
    
private voidscheduleUpdateClientsIfNeededLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        final int clientState = userState.getClientState();
        if (userState.mLastSentClientState != clientState
                && (mGlobalClients.getRegisteredCallbackCount() > 0
                        || userState.mClients.getRegisteredCallbackCount() > 0)) {
            userState.mLastSentClientState = clientState;
            mMainHandler.obtainMessage(MainHandler.MSG_SEND_STATE_TO_CLIENTS,
                    clientState, userState.mUserId) .sendToTarget();
        }
    
private voidscheduleUpdateInputFilter(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        mMainHandler.obtainMessage(MainHandler.MSG_UPDATE_INPUT_FILTER, userState).sendToTarget();
    
public booleansendAccessibilityEvent(android.view.accessibility.AccessibilityEvent event, int userId)

        synchronized (mLock) {
            // We treat calls from a profile as if made by its parent as profiles
            // share the accessibility state of the parent. The call below
            // performs the current profile parent resolution..
            final int resolvedUserId = mSecurityPolicy
                    .resolveCallingUserIdEnforcingPermissionsLocked(userId);
            // This method does nothing for a background user.
            if (resolvedUserId != mCurrentUserId) {
                return true; // yes, recycle the event
            }
            if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
                mSecurityPolicy.updateActiveAndAccessibilityFocusedWindowLocked(event.getWindowId(),
                        event.getSourceNodeId(), event.getEventType());
                mSecurityPolicy.updateEventSourceLocked(event);
                notifyAccessibilityServicesDelayedLocked(event, false);
                notifyAccessibilityServicesDelayedLocked(event, true);
            }
            if (mHasInputFilter && mInputFilter != null) {
                mMainHandler.obtainMessage(MainHandler.MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER,
                        AccessibilityEvent.obtain(event)).sendToTarget();
            }
            event.recycle();
            getUserStateLocked(resolvedUserId).mHandledFeedbackTypes = 0;
        }
        return (OWN_PROCESS_ID != Binder.getCallingPid());
    
private voidshowEnableTouchExplorationDialog(com.android.server.accessibility.AccessibilityManagerService$Service service)

        synchronized (mLock) {
            String label = service.mResolveInfo.loadLabel(
            mContext.getPackageManager()).toString();

            final UserState state = getCurrentUserStateLocked();
            if (state.mIsTouchExplorationEnabled) {
                return;
            }
            if (mEnableTouchExplorationDialog != null
                    && mEnableTouchExplorationDialog.isShowing()) {
                return;
            }
            mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext)
                .setIconAttribute(android.R.attr.alertDialogIcon)
                .setPositiveButton(android.R.string.ok, new OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
                         // The user allowed the service to toggle touch exploration.
                         state.mTouchExplorationGrantedServices.add(service.mComponentName);
                         persistComponentNamesToSettingLocked(
                                 Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
                                 state.mTouchExplorationGrantedServices, state.mUserId);
                         // Enable touch exploration.
                         UserState userState = getUserStateLocked(service.mUserId);
                         userState.mIsTouchExplorationEnabled = true;
                         Settings.Secure.putIntForUser(mContext.getContentResolver(),
                                 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1,
                                 service.mUserId);
                         onUserStateChangedLocked(userState);
                     }
                 })
                 .setNegativeButton(android.R.string.cancel, new OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
                         dialog.dismiss();
                     }
                 })
                 .setTitle(R.string.enable_explore_by_touch_warning_title)
                 .setMessage(mContext.getString(
                         R.string.enable_explore_by_touch_warning_message, label))
                 .create();
             mEnableTouchExplorationDialog.getWindow().setType(
                     WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
             mEnableTouchExplorationDialog.getWindow().getAttributes().privateFlags
                     |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
             mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true);
             mEnableTouchExplorationDialog.show();
        }
    
private voidswitchUser(int userId)

        synchronized (mLock) {
            if (mCurrentUserId == userId && mInitialized) {
                return;
            }

            // Disconnect from services for the old user.
            UserState oldUserState = getCurrentUserStateLocked();
            oldUserState.onSwitchToAnotherUser();

            // Disable the local managers for the old user.
            if (oldUserState.mClients.getRegisteredCallbackCount() > 0) {
                mMainHandler.obtainMessage(MainHandler.MSG_SEND_CLEARED_STATE_TO_CLIENTS_FOR_USER,
                        oldUserState.mUserId, 0).sendToTarget();
            }

            // Announce user changes only if more that one exist.
            UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
            final boolean announceNewUser = userManager.getUsers().size() > 1;

            // The user changed.
            mCurrentUserId = userId;

            UserState userState = getCurrentUserStateLocked();
            if (userState.mUiAutomationService != null) {
                // Switching users disables the UI automation service.
                userState.mUiAutomationService.binderDied();
            }

            readConfigurationForUserStateLocked(userState);
            // Even if reading did not yield change, we have to update
            // the state since the context in which the current user
            // state was used has changed since it was inactive.
            onUserStateChangedLocked(userState);

            if (announceNewUser) {
                // Schedule announcement of the current user if needed.
                mMainHandler.sendEmptyMessageDelayed(MainHandler.MSG_ANNOUNCE_NEW_USER_IF_NEEDED,
                        WAIT_FOR_USER_STATE_FULLY_INITIALIZED_MILLIS);
            }
        }
    
public voidtemporaryEnableAccessibilityStateUntilKeyguardRemoved(android.content.ComponentName service, boolean touchExplorationEnabled)

        mSecurityPolicy.enforceCallingPermission(
                Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY,
                TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED);
        if (!mWindowManagerService.isKeyguardLocked()) {
            return;
        }
        synchronized (mLock) {
            // Set the temporary state.
            UserState userState = getCurrentUserStateLocked();

            // This is a nop if UI automation is enabled.
            if (userState.mUiAutomationService != null) {
                return;
            }

            userState.mIsAccessibilityEnabled = true;
            userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
            userState.mIsEnhancedWebAccessibilityEnabled = false;
            userState.mIsDisplayMagnificationEnabled = false;
            userState.mEnabledServices.clear();
            userState.mEnabledServices.add(service);
            userState.mBindingServices.clear();
            userState.mTouchExplorationGrantedServices.clear();
            userState.mTouchExplorationGrantedServices.add(service);

            // User the current state instead settings.
            onUserStateChangedLocked(userState);
        }
    
private voidunbindAllServicesLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        List<Service> services = userState.mBoundServices;
        for (int i = 0, count = services.size(); i < count; i++) {
            Service service = services.get(i);
            if (service.unbindLocked()) {
                i--;
                count--;
            }
        }
    
public voidunregisterUiTestAutomationService(android.accessibilityservice.IAccessibilityServiceClient serviceClient)

        synchronized (mLock) {
            UserState userState = getCurrentUserStateLocked();
            // Automation service is not bound, so pretend it died to perform clean up.
            if (userState.mUiAutomationService != null
                    && serviceClient != null
                    && userState.mUiAutomationService.mServiceInterface != null
                    && userState.mUiAutomationService.mServiceInterface.asBinder()
                    == serviceClient.asBinder()) {
                userState.mUiAutomationService.binderDied();
            } else {
                throw new IllegalStateException("UiAutomationService " + serviceClient
                        + " not registered!");
            }
        }
    
private voidupdateAccessibilityFocusBehaviorLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        // If there is no service that can operate with interactive windows
        // then we keep the old behavior where a window loses accessibility
        // focus if it is no longer active. This still changes the behavior
        // for services that do not operate with interactive windows and run
        // at the same time as the one(s) which does. In practice however,
        // there is only one service that uses accessibility focus and it
        // is typically the one that operates with interactive windows, So,
        // this is fine. Note that to allow a service to work across windows
        // we have to allow accessibility focus stay in any of them. Sigh...
        List<Service> boundServices = userState.mBoundServices;
        final int boundServiceCount = boundServices.size();
        for (int i = 0; i < boundServiceCount; i++) {
            Service boundService = boundServices.get(i);
            if (boundService.canRetrieveInteractiveWindowsLocked()) {
                userState.mAccessibilityFocusOnlyInActiveWindow = false;
                return;
            }
        }
        userState.mAccessibilityFocusOnlyInActiveWindow = true;
    
private voidupdateDisplayColorAdjustmentSettingsLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        DisplayAdjustmentUtils.applyAdjustments(mContext, userState.mUserId);
    
private voidupdateEnhancedWebAccessibilityLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        boolean enabled = false;
        final int serviceCount = userState.mBoundServices.size();
        for (int i = 0; i < serviceCount; i++) {
            Service service = userState.mBoundServices.get(i);
            if (canRequestAndRequestsEnhancedWebAccessibilityLocked(service)) {
                enabled = true;
                break;
            }
        }
        if (enabled != userState.mIsEnhancedWebAccessibilityEnabled) {
            userState.mIsEnhancedWebAccessibilityEnabled = enabled;
            Settings.Secure.putIntForUser(mContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, enabled ? 1 : 0,
                    userState.mUserId);
        }
    
private voidupdateFilterKeyEventsLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        final int serviceCount = userState.mBoundServices.size();
        for (int i = 0; i < serviceCount; i++) {
            Service service = userState.mBoundServices.get(i);
            if (service.mRequestFilterKeyEvents
                    && (service.mAccessibilityServiceInfo.getCapabilities()
                            & AccessibilityServiceInfo
                            .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) {
                userState.mIsFilterKeyEventsEnabled = true;
                return;
            }
        }
        userState.mIsFilterKeyEventsEnabled = false;
    
private voidupdateInputFilter(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        boolean setInputFilter = false;
        AccessibilityInputFilter inputFilter = null;
        synchronized (mLock) {
            int flags = 0;
            if (userState.mIsDisplayMagnificationEnabled) {
                flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
            }
            // Touch exploration without accessibility makes no sense.
            if (userState.mIsAccessibilityEnabled && userState.mIsTouchExplorationEnabled) {
                flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
            }
            if (userState.mIsFilterKeyEventsEnabled) {
                flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
            }
            if (flags != 0) {
                if (!mHasInputFilter) {
                    mHasInputFilter = true;
                    if (mInputFilter == null) {
                        mInputFilter = new AccessibilityInputFilter(mContext,
                                AccessibilityManagerService.this);
                    }
                    inputFilter = mInputFilter;
                    setInputFilter = true;
                }
                mInputFilter.setEnabledFeatures(flags);
            } else {
                if (mHasInputFilter) {
                    mHasInputFilter = false;
                    mInputFilter.disableFeatures();
                    inputFilter = null;
                    setInputFilter = true;
                }
            }
        }
        if (setInputFilter) {
            mWindowManagerService.setInputFilter(inputFilter);
        }
    
private voidupdateLegacyCapabilitiesLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        // Up to JB-MR1 we had a white list with services that can enable touch
        // exploration. When a service is first started we show a dialog to the
        // use to get a permission to white list the service.
        final int installedServiceCount = userState.mInstalledServices.size();
        for (int i = 0; i < installedServiceCount; i++) {
            AccessibilityServiceInfo serviceInfo = userState.mInstalledServices.get(i);
            ResolveInfo resolveInfo = serviceInfo.getResolveInfo();
            if ((serviceInfo.getCapabilities()
                        & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION) == 0
                    && resolveInfo.serviceInfo.applicationInfo.targetSdkVersion
                        <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                ComponentName componentName = new ComponentName(
                        resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
                if (userState.mTouchExplorationGrantedServices.contains(componentName)) {
                    serviceInfo.setCapabilities(serviceInfo.getCapabilities()
                            | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION);
                }
            }
        }
    
private voidupdateServicesLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        if (userState.mIsAccessibilityEnabled) {
            manageServicesLocked(userState);
        } else {
            unbindAllServicesLocked(userState);
        }
    
private voidupdateTouchExplorationLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        boolean enabled = false;
        final int serviceCount = userState.mBoundServices.size();
        for (int i = 0; i < serviceCount; i++) {
            Service service = userState.mBoundServices.get(i);
            if (canRequestAndRequestsTouchExplorationLocked(service)) {
                enabled = true;
                break;
            }
        }
        if (enabled != userState.mIsTouchExplorationEnabled) {
            userState.mIsTouchExplorationEnabled = enabled;
            Settings.Secure.putIntForUser(mContext.getContentResolver(),
                    Settings.Secure.TOUCH_EXPLORATION_ENABLED, enabled ? 1 : 0,
                    userState.mUserId);
        }
    
private voidupdateWindowsForAccessibilityCallbackLocked(com.android.server.accessibility.AccessibilityManagerService$UserState userState)

        if (userState.mIsAccessibilityEnabled) {
            // We observe windows for accessibility only if there is at least
            // one bound service that can retrieve window content that specified
            // it is interested in accessing such windows. For services that are
            // binding we do an update pass after each bind event, so we run this
            // code and register the callback if needed.
            boolean boundServiceCanRetrieveInteractiveWindows = false;

            List<Service> boundServices = userState.mBoundServices;
            final int boundServiceCount = boundServices.size();
            for (int i = 0; i < boundServiceCount; i++) {
                Service boundService = boundServices.get(i);
                if (boundService.canRetrieveInteractiveWindowsLocked()) {
                    boundServiceCanRetrieveInteractiveWindows = true;
                    break;
                }
            }

            if (boundServiceCanRetrieveInteractiveWindows) {
                if (mWindowsForAccessibilityCallback == null) {
                    mWindowsForAccessibilityCallback = new WindowsForAccessibilityCallback();
                    mWindowManagerService.setWindowsForAccessibilityCallback(
                            mWindowsForAccessibilityCallback);
                }
                return;
            }
        }

        if (mWindowsForAccessibilityCallback != null) {
            mWindowsForAccessibilityCallback = null;
            mWindowManagerService.setWindowsForAccessibilityCallback(null);
            // Drop all windows we know about.
            mSecurityPolicy.clearWindowsLocked();
        }