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

Commit a4725efd authored by Svetoslav's avatar Svetoslav Committed by Svetoslav Ganov
Browse files

Improve the window query API performamce.

We are caching the window data in the accessibility service process.
When windows change we were sending the dalta of the windows the
service knows about. To make this work when the app asked for all
windows we had to call into the system as new windows may have
appeared. This was slow.

Now we are telling the service some windows change and if it gets
the windows we cache them. We call into the system only on a cache
miss and evict all windows from the cache on window change event.
We do not evict the nodes of the window as the former may have
just moved. If views in a window change they fire accessibility
events that trigger the correct eviction.

Change-Id: I586a72a2497b0d44a75288fa758e7e88817f3300
parent 0dcebb92
Loading
Loading
Loading
Loading
+0 −36
Original line number Original line Diff line number Diff line
@@ -23,7 +23,6 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Looper;
import android.os.Message;
import android.os.Message;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.Log;
import android.view.KeyEvent;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEvent;
@@ -659,16 +658,12 @@ public abstract class AccessibilityService extends Service {
     */
     */
    public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
    public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
            implements HandlerCaller.Callback {
            implements HandlerCaller.Callback {

        static final int NO_ID = -1;

        private static final int DO_SET_SET_CONNECTION = 1;
        private static final int DO_SET_SET_CONNECTION = 1;
        private static final int DO_ON_INTERRUPT = 2;
        private static final int DO_ON_INTERRUPT = 2;
        private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
        private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
        private static final int DO_ON_GESTURE = 4;
        private static final int DO_ON_GESTURE = 4;
        private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
        private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
        private static final int DO_ON_KEY_EVENT = 6;
        private static final int DO_ON_KEY_EVENT = 6;
        private static final int DO_ON_WINDOWS_CHANGED = 7;


        private final HandlerCaller mCaller;
        private final HandlerCaller mCaller;


@@ -714,12 +709,6 @@ public abstract class AccessibilityService extends Service {
            mCaller.sendMessage(message);
            mCaller.sendMessage(message);
        }
        }


        @Override
        public void onWindowsChanged(int[] windowIds) {
            Message message = mCaller.obtainMessageO(DO_ON_WINDOWS_CHANGED, windowIds);
            mCaller.sendMessage(message);
        }

        @Override
        @Override
        public void executeMessage(Message message) {
        public void executeMessage(Message message) {
            switch (message.what) {
            switch (message.what) {
@@ -791,31 +780,6 @@ public abstract class AccessibilityService extends Service {
                    }
                    }
                } return;
                } return;


                case DO_ON_WINDOWS_CHANGED: {
                    final int[] windowIds = (int[]) message.obj;

                    // Update the cached windows first.
                    // NOTE: The cache will hold on to the windows so do not recycle.
                    if (windowIds != null) {
                        AccessibilityInteractionClient.getInstance().removeWindows(windowIds);
                    }

                    // Let the client know the windows changed.
                    AccessibilityEvent event = AccessibilityEvent.obtain(
                            AccessibilityEvent.TYPE_WINDOWS_CHANGED);
                    event.setEventTime(SystemClock.uptimeMillis());
                    event.setSealed(true);

                    mCallback.onAccessibilityEvent(event);

                    // Make sure the event is recycled.
                    try {
                        event.recycle();
                    } catch (IllegalStateException ise) {
                        /* ignore - best effort */
                    }
                } break;

                default :
                default :
                    Log.w(LOG_TAG, "Unknown message type " + message.what);
                    Log.w(LOG_TAG, "Unknown message type " + message.what);
            }
            }
+0 −2
Original line number Original line Diff line number Diff line
@@ -39,6 +39,4 @@ import android.view.KeyEvent;
    void clearAccessibilityCache();
    void clearAccessibilityCache();


    void onKeyEvent(in KeyEvent event, int sequence);
    void onKeyEvent(in KeyEvent event, int sequence);

    void onWindowsChanged(in int[] changedWindowIds);
}
}
+10 −8
Original line number Original line Diff line number Diff line
@@ -58,20 +58,18 @@ final class AccessibilityCache {
        }
        }
    }
    }


    public void removeWindows(int[] windowIds) {
    public void clearWindows() {
        synchronized (mLock) {
        synchronized (mLock) {
            final int windowCount = windowIds.length;
            final int windowCount = mWindowCache.size();
            for (int i = 0; i < windowCount; i++) {
            for (int i = windowCount - 1; i >= 0; i--) {
                final int windowId = windowIds[i];
                AccessibilityWindowInfo window = mWindowCache.valueAt(i);
                AccessibilityWindowInfo window = mWindowCache.get(windowId);
                if (window != null) {
                if (window != null) {
                    if (DEBUG) {
                    if (DEBUG) {
                        Log.i(LOG_TAG, "Removing window: " + windowId);
                        Log.i(LOG_TAG, "Removing window: " + window.getId());
                    }
                    }
                    window.recycle();
                    window.recycle();
                    mWindowCache.remove(windowId);
                    mWindowCache.removeAt(i);
                }
                }
                clearNodesForWindowLocked(windowIds[i]);
            }
            }
        }
        }
    }
    }
@@ -111,6 +109,10 @@ final class AccessibilityCache {
                case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
                case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
                    clearSubTreeLocked(event.getWindowId(), event.getSourceNodeId());
                    clearSubTreeLocked(event.getWindowId(), event.getSourceNodeId());
                } break;
                } break;

                case AccessibilityEvent.TYPE_WINDOWS_CHANGED: {
                    clearWindows();
                } break;
            }
            }
        }
        }


+16 −28
Original line number Original line Diff line number Diff line
@@ -84,7 +84,7 @@ public final class AccessibilityInteractionClient
    private static final Object sStaticLock = new Object();
    private static final Object sStaticLock = new Object();


    private static final LongSparseArray<AccessibilityInteractionClient> sClients =
    private static final LongSparseArray<AccessibilityInteractionClient> sClients =
        new LongSparseArray<AccessibilityInteractionClient>();
        new LongSparseArray<>();


    private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
    private final AtomicInteger mInteractionIdCounter = new AtomicInteger();


@@ -101,7 +101,7 @@ public final class AccessibilityInteractionClient
    private Message mSameThreadMessage;
    private Message mSameThreadMessage;


    private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
    private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
        new SparseArray<IAccessibilityServiceConnection>();
        new SparseArray<>();


    private static final AccessibilityCache sAccessibilityCache =
    private static final AccessibilityCache sAccessibilityCache =
        new AccessibilityCache();
        new AccessibilityCache();
@@ -162,19 +162,6 @@ public final class AccessibilityInteractionClient
                false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
                false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
    }
    }


    /**
     * Gets the root {@link AccessibilityNodeInfo} in a given window.
     *
     * @param connectionId The id of a connection for interacting with the system.
     * @param windowId The window id.
     * @return The root {@link AccessibilityNodeInfo} if found, null otherwise.
     */
    public AccessibilityNodeInfo getRootInWindow(int connectionId, int windowId) {
        return findAccessibilityNodeInfoByAccessibilityId(connectionId, windowId,
                AccessibilityNodeInfo.ROOT_NODE_ID, false,
                AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
    }

    /**
    /**
     * Gets the info for a window.
     * Gets the info for a window.
     *
     *
@@ -225,11 +212,17 @@ public final class AccessibilityInteractionClient
        try {
        try {
            IAccessibilityServiceConnection connection = getConnection(connectionId);
            IAccessibilityServiceConnection connection = getConnection(connectionId);
            if (connection != null) {
            if (connection != null) {
                // The system is just sending data for windows that we introspected
                List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows();
                // and changed but not ones that appeared, so we have to always call
                if (windows != null) {
                // into the system process. This is less expensice as opposed to
                    if (DEBUG) {
                // sending all windows on every window change.
                        Log.i(LOG_TAG, "Windows cache hit");
                List<AccessibilityWindowInfo> windows = connection.getWindows();
                    }
                    return windows;
                }
                if (DEBUG) {
                    Log.i(LOG_TAG, "Windows cache miss");
                }
                windows = connection.getWindows();
                if (windows != null) {
                if (windows != null) {
                    final int windowCount = windows.size();
                    final int windowCount = windows.size();
                    for (int i = 0; i < windowCount; i++) {
                    for (int i = 0; i < windowCount; i++) {
@@ -534,10 +527,6 @@ public final class AccessibilityInteractionClient
        sAccessibilityCache.onAccessibilityEvent(event);
        sAccessibilityCache.onAccessibilityEvent(event);
    }
    }


    public void removeWindows(int[] windowIds) {
        sAccessibilityCache.removeWindows(windowIds);
    }

    /**
    /**
     * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
     * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
     *
     *
@@ -603,8 +592,7 @@ public final class AccessibilityInteractionClient
                    // instantiate new result list to avoid passing internal instances to clients.
                    // instantiate new result list to avoid passing internal instances to clients.
                    final boolean isIpcCall = (Binder.getCallingPid() != Process.myPid());
                    final boolean isIpcCall = (Binder.getCallingPid() != Process.myPid());
                    if (!isIpcCall) {
                    if (!isIpcCall) {
                        mFindAccessibilityNodeInfosResult =
                        mFindAccessibilityNodeInfosResult = new ArrayList<>(infos);
                            new ArrayList<AccessibilityNodeInfo>(infos);
                    } else {
                    } else {
                        mFindAccessibilityNodeInfosResult = infos;
                        mFindAccessibilityNodeInfosResult = infos;
                    }
                    }
@@ -795,8 +783,8 @@ public final class AccessibilityInteractionClient
            Log.e(LOG_TAG, "No root.");
            Log.e(LOG_TAG, "No root.");
        }
        }
        // Check for duplicates.
        // Check for duplicates.
        HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
        HashSet<AccessibilityNodeInfo> seen = new HashSet<>();
        Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
        Queue<AccessibilityNodeInfo> fringe = new LinkedList<>();
        fringe.add(root);
        fringe.add(root);
        while (!fringe.isEmpty()) {
        while (!fringe.isEmpty()) {
            AccessibilityNodeInfo current = fringe.poll();
            AccessibilityNodeInfo current = fringe.poll();
+45 −130
Original line number Original line Diff line number Diff line
@@ -62,7 +62,6 @@ import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.LongArray;
import android.util.Pools.Pool;
import android.util.Pools.Pool;
import android.util.Pools.SimplePool;
import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.Slog;
@@ -114,8 +113,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
 * accessed only by the system. The task of this service is to be a centralized
 * 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
 * event dispatch for {@link AccessibilityEvent}s generated across all processes
 * on the device. Events are dispatched to {@link AccessibilityService}s.
 * on the device. Events are dispatched to {@link AccessibilityService}s.
 *
 * @hide
 */
 */
public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public class AccessibilityManagerService extends IAccessibilityManager.Stub {


@@ -155,13 +152,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
    private final Object mLock = new Object();
    private final Object mLock = new Object();


    private final Pool<PendingEvent> mPendingEventPool =
    private final Pool<PendingEvent> mPendingEventPool =
            new SimplePool<PendingEvent>(MAX_POOL_SIZE);
            new SimplePool<>(MAX_POOL_SIZE);


    private final SimpleStringSplitter mStringColonSplitter =
    private final SimpleStringSplitter mStringColonSplitter =
            new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
            new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);


    private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList =
    private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList =
            new ArrayList<AccessibilityServiceInfo>();
            new ArrayList<>();


    private final Rect mTempRect = new Rect();
    private final Rect mTempRect = new Rect();


@@ -183,27 +180,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {


    private boolean mHasInputFilter;
    private boolean mHasInputFilter;


    private final Set<ComponentName> mTempComponentNameSet = new HashSet<ComponentName>();
    private final Set<ComponentName> mTempComponentNameSet = new HashSet<>();


    private final List<AccessibilityServiceInfo> mTempAccessibilityServiceInfoList =
    private final List<AccessibilityServiceInfo> mTempAccessibilityServiceInfoList =
            new ArrayList<AccessibilityServiceInfo>();
            new ArrayList<>();


    private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients =
    private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients =
            new RemoteCallbackList<IAccessibilityManagerClient>();
            new RemoteCallbackList<>();


    private final SparseArray<AccessibilityConnectionWrapper> mGlobalInteractionConnections =
    private final SparseArray<AccessibilityConnectionWrapper> mGlobalInteractionConnections =
            new SparseArray<AccessibilityConnectionWrapper>();
            new SparseArray<>();


    private final SparseArray<IBinder> mGlobalWindowTokens = new SparseArray<IBinder>();
    private final SparseArray<IBinder> mGlobalWindowTokens = new SparseArray<>();


    private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
    private final SparseArray<UserState> mUserStates = new SparseArray<>();


    private final UserManager mUserManager;
    private final UserManager mUserManager;


    private int mCurrentUserId = UserHandle.USER_OWNER;
    private int mCurrentUserId = UserHandle.USER_OWNER;


    private final LongArray mTempLongArray = new LongArray();

    //TODO: Remove this hack
    //TODO: Remove this hack
    private boolean mInitialized;
    private boolean mInitialized;


@@ -439,10 +434,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            // to clients as being installed - it really is not.
            // to clients as being installed - it really is not.
            UserState userState = getUserStateLocked(resolvedUserId);
            UserState userState = getUserStateLocked(resolvedUserId);
            if (userState.mUiAutomationService != null) {
            if (userState.mUiAutomationService != null) {
                List<AccessibilityServiceInfo> installedServices =
                List<AccessibilityServiceInfo> installedServices = new ArrayList<>();
                        new ArrayList<AccessibilityServiceInfo>();
                installedServices.addAll(userState.mInstalledServices);
                installedServices.addAll(userState.mInstalledServices);
                installedServices.remove(userState.mUiAutomationService);
                installedServices.remove(userState.mUiAutomationService.mAccessibilityServiceInfo);
                return installedServices;
                return installedServices;
            }
            }
            return userState.mInstalledServices;
            return userState.mInstalledServices;
@@ -655,7 +649,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            // Automation service is not bound, so pretend it died to perform clean up.
            // Automation service is not bound, so pretend it died to perform clean up.
            if (userState.mUiAutomationService != null
            if (userState.mUiAutomationService != null
                    && serviceClient != null
                    && serviceClient != null
                    && userState.mUiAutomationService != null
                    && userState.mUiAutomationService.mServiceInterface != null
                    && userState.mUiAutomationService.mServiceInterface != null
                    && userState.mUiAutomationService.mServiceInterface.asBinder()
                    && userState.mUiAutomationService.mServiceInterface.asBinder()
                    == serviceClient.asBinder()) {
                    == serviceClient.asBinder()) {
@@ -907,16 +900,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        }
        }
    }
    }


    private void notifyWindowsChangedLocked(List<AccessibilityWindowInfo> windows) {
        UserState state = getCurrentUserStateLocked();
        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
            Service service = state.mBoundServices.get(i);
            if (mSecurityPolicy.canRetrieveWindowsLocked(service)) {
                service.notifyWindowsChangedLocked(windows);
            }
        }
    }

    /**
    /**
     * Removes an AccessibilityInteractionConnection.
     * Removes an AccessibilityInteractionConnection.
     *
     *
@@ -961,10 +944,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            try {
            try {
                accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
                accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
                mTempAccessibilityServiceInfoList.add(accessibilityServiceInfo);
                mTempAccessibilityServiceInfoList.add(accessibilityServiceInfo);
            } catch (XmlPullParserException xppe) {
            } catch (XmlPullParserException | IOException xppe) {
                Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe);
                Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe);
            } catch (IOException ioe) {
                Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", ioe);
            }
            }
        }
        }


@@ -1035,7 +1016,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            // An out of bounds exception can happen if services are going away
            // An out of bounds exception can happen if services are going away
            // as the for loop is running. If that happens, just bail because
            // as the for loop is running. If that happens, just bail because
            // there are no more services to notify.
            // there are no more services to notify.
            return;
        }
        }
    }
    }


@@ -1053,7 +1033,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
     * Removes a service.
     * Removes a service.
     *
     *
     * @param service The service.
     * @param service The service.
     * @return True if the service was removed, false otherwise.
     */
     */
    private void removeServiceLocked(Service service, UserState userState) {
    private void removeServiceLocked(Service service, UserState userState) {
        userState.mBoundServices.remove(service);
        userState.mBoundServices.remove(service);
@@ -1093,7 +1072,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        }
        }


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


        if (packageNames.isEmpty() || packageNames.contains(packageName)) {
        if (packageNames.isEmpty() || packageNames.contains(packageName)) {
            int feedbackType = service.mFeedbackType;
            int feedbackType = service.mFeedbackType;
@@ -1386,8 +1366,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {


        if (mWindowsForAccessibilityCallback != null) {
        if (mWindowsForAccessibilityCallback != null) {
            mWindowsForAccessibilityCallback = null;
            mWindowsForAccessibilityCallback = null;
            mWindowManagerService.setWindowsForAccessibilityCallback(
            mWindowManagerService.setWindowsForAccessibilityCallback(null);
                    mWindowsForAccessibilityCallback);
        }
        }
    }
    }


@@ -1437,8 +1416,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
    }
    }


    private boolean readConfigurationForUserStateLocked(UserState userState) {
    private boolean readConfigurationForUserStateLocked(UserState userState) {
        boolean somthingChanged = false;
        boolean somthingChanged = readAccessibilityEnabledSettingLocked(userState);
        somthingChanged |= readAccessibilityEnabledSettingLocked(userState);
        somthingChanged |= readInstalledAccessibilityServiceLocked(userState);
        somthingChanged |= readInstalledAccessibilityServiceLocked(userState);
        somthingChanged |= readEnabledAccessibilityServicesLocked(userState);
        somthingChanged |= readEnabledAccessibilityServicesLocked(userState);
        somthingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState);
        somthingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState);
@@ -1868,7 +1846,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {


        int mFeedbackType;
        int mFeedbackType;


        Set<String> mPackageNames = new HashSet<String>();
        Set<String> mPackageNames = new HashSet<>();


        boolean mIsDefault;
        boolean mIsDefault;


@@ -1890,20 +1868,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {


        boolean mIsAutomation;
        boolean mIsAutomation;


        final Rect mTempBounds1 = new Rect();

        final Rect mTempBounds2 = new Rect();

        final ResolveInfo mResolveInfo;
        final ResolveInfo mResolveInfo;


        // the events pending events to be dispatched to this service
        // the events pending events to be dispatched to this service
        final SparseArray<AccessibilityEvent> mPendingEvents =
        final SparseArray<AccessibilityEvent> mPendingEvents =
            new SparseArray<AccessibilityEvent>();
            new SparseArray<>();


        final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();
        final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();


        final SparseArray<AccessibilityWindowInfo> mIntrospectedWindows =
        final SparseArray<AccessibilityWindowInfo> mIntrospectedWindows =
                new SparseArray<AccessibilityWindowInfo>();
                new SparseArray<>();


        boolean mWasConnectedAndDied;
        boolean mWasConnectedAndDied;


@@ -2106,7 +2080,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                if (!permissionGranted) {
                if (!permissionGranted) {
                    return null;
                    return null;
                }
                }
                List<AccessibilityWindowInfo> windows = new ArrayList<AccessibilityWindowInfo>();
                List<AccessibilityWindowInfo> windows = new ArrayList<>();
                final int windowCount = mSecurityPolicy.mWindows.size();
                final int windowCount = mSecurityPolicy.mWindows.size();
                for (int i = 0; i < windowCount; i++) {
                for (int i = 0; i < windowCount; i++) {
                    AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(i);
                    AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(i);
@@ -2646,51 +2620,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            }
            }
        }
        }


        public void notifyWindowsChangedLocked(List<AccessibilityWindowInfo> windows) {
            LongArray changedWindows = mTempLongArray;
            changedWindows.clear();

            // Figure out which windows the service cares about changed.
            final int windowCount = windows.size();
            for (int i = 0; i < windowCount; i++) {
                AccessibilityWindowInfo newState = windows.get(i);
                final int windowId = newState.getId();
                AccessibilityWindowInfo oldState = mIntrospectedWindows.get(windowId);
                if (oldState != null && oldState.changed(newState)) {
                    oldState.recycle();
                    mIntrospectedWindows.put(newState.getId(),
                            AccessibilityWindowInfo.obtain(newState));
                    changedWindows.add(windowId);
                }
            }

            // Figure out which windows the service cares about went away.
            final int introspectedWindowCount = mIntrospectedWindows.size();
            for (int i = introspectedWindowCount - 1; i >= 0; i--) {
                AccessibilityWindowInfo window = mIntrospectedWindows.valueAt(i);
                if (changedWindows.indexOf(window.getId()) < 0 && !windows.contains(window)) {
                    changedWindows.add(window.getId());
                    mIntrospectedWindows.removeAt(i);
                    window.recycle();
                }
            }

            int[] windowIds = null;

            final int changedWindowCount = changedWindows.size();
            if (changedWindowCount > 0) {
                windowIds = new int[changedWindowCount];
                for (int i = 0; i < changedWindowCount; i++) {
                    // Cast is fine as we use long array to cache ints.
                    windowIds[i] = (int) changedWindows.get(i);
                }
                changedWindows.clear();
            }

            mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_WINDOWS_CHANGED,
                    windowIds).sendToTarget();
        }

        private void notifyGestureInternal(int gestureId) {
        private void notifyGestureInternal(int gestureId) {
            final IAccessibilityServiceClient listener;
            final IAccessibilityServiceClient listener;
            synchronized (mLock) {
            synchronized (mLock) {
@@ -2725,20 +2654,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            }
            }
        }
        }


        private void notifyWindowsChangedInternal(int[] windowIds) {
            final IAccessibilityServiceClient listener;
            synchronized (mLock) {
                listener = mServiceInterface;
            }
            if (listener != null) {
                try {
                    listener.onWindowsChanged(windowIds);
                } catch (RemoteException re) {
                    Slog.e(LOG_TAG, "Error during sending windows to: " + mService, re);
                }
            }
        }

        private void sendDownAndUpKeyEvents(int keyCode) {
        private void sendDownAndUpKeyEvents(int keyCode) {
            final long token = Binder.clearCallingIdentity();
            final long token = Binder.clearCallingIdentity();


@@ -2844,7 +2759,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            public static final int MSG_ON_KEY_EVENT = 2;
            public static final int MSG_ON_KEY_EVENT = 2;
            public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 3;
            public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 3;
            public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4;
            public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4;
            public static final int MSG_ON_WINDOWS_CHANGED = 5;


            public InvocationHandler(Looper looper) {
            public InvocationHandler(Looper looper) {
                super(looper, null, true);
                super(looper, null, true);
@@ -2874,11 +2788,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                        setOnKeyEventResult(false, eventState.sequence);
                        setOnKeyEventResult(false, eventState.sequence);
                    } break;
                    } break;


                    case MSG_ON_WINDOWS_CHANGED: {
                        final int[] windowIds = (int[]) message.obj;
                        notifyWindowsChangedInternal(windowIds);
                    } break;

                    default: {
                    default: {
                        throw new IllegalArgumentException("Unknown message: " + type);
                        throw new IllegalArgumentException("Unknown message: " + type);
                    }
                    }
@@ -3028,14 +2937,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        @Override
        @Override
        public void onWindowsForAccessibilityChanged(List<WindowInfo> windows) {
        public void onWindowsForAccessibilityChanged(List<WindowInfo> windows) {
            synchronized (mLock) {
            synchronized (mLock) {
                List<WindowInfo> receivedWindows = (List<WindowInfo>) windows;

                // Populate the windows to report.
                // Populate the windows to report.
                List<AccessibilityWindowInfo> reportedWindows =
                List<AccessibilityWindowInfo> reportedWindows = new ArrayList<>();
                        new ArrayList<AccessibilityWindowInfo>();
                final int receivedWindowCount = windows.size();
                final int receivedWindowCount = receivedWindows.size();
                for (int i = 0; i < receivedWindowCount; i++) {
                for (int i = 0; i < receivedWindowCount; i++) {
                    WindowInfo receivedWindow = receivedWindows.get(i);
                    WindowInfo receivedWindow = windows.get(i);
                    AccessibilityWindowInfo reportedWindow = populateReportedWindow(
                    AccessibilityWindowInfo reportedWindow = populateReportedWindow(
                            receivedWindow);
                            receivedWindow);
                    if (reportedWindow != null) {
                    if (reportedWindow != null) {
@@ -3224,8 +3130,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
            | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
            | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;


        public final List<AccessibilityWindowInfo> mWindows =
        public final List<AccessibilityWindowInfo> mWindows = new ArrayList<>();
                new ArrayList<AccessibilityWindowInfo>();


        public int mActiveWindowId = INVALID_WINDOW_ID;
        public int mActiveWindowId = INVALID_WINDOW_ID;
        public int mFocusedWindowId = INVALID_WINDOW_ID;
        public int mFocusedWindowId = INVALID_WINDOW_ID;
@@ -3265,7 +3170,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START:
                case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START:
                case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
                case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
                case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
                case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
                case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
                case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT:
                // Also windows changing should always be anounced.
                case AccessibilityEvent.TYPE_WINDOWS_CHANGED: {
                    return true;
                    return true;
                }
                }
                // All events for changes in window content should be
                // All events for changes in window content should be
@@ -3327,7 +3234,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                }
                }
            }
            }


            notifyWindowsChangedLocked(mWindows);
            notifyWindowsChanged();


            // If we are delaying a window state change event as the window
            // If we are delaying a window state change event as the window
            // source was showing when it was fired, now is the time to send.
            // source was showing when it was fired, now is the time to send.
@@ -3452,8 +3359,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                    AccessibilityWindowInfo window = mWindows.get(i);
                    AccessibilityWindowInfo window = mWindows.get(i);
                    window.setActive(window.getId() == windowId);
                    window.setActive(window.getId() == windowId);
                }
                }
                notifyWindowsChangedLocked(mWindows);
                notifyWindowsChanged();
            }
        }
        }

        private void notifyWindowsChanged() {
            // Let the client know the windows changed.
            AccessibilityEvent event = AccessibilityEvent.obtain(
                    AccessibilityEvent.TYPE_WINDOWS_CHANGED);
            event.setEventTime(SystemClock.uptimeMillis());
            sendAccessibilityEvent(event, mCurrentUserId);
        }
        }


        public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
        public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
@@ -3568,30 +3483,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        // Non-transient state.
        // Non-transient state.


        public final RemoteCallbackList<IAccessibilityManagerClient> mClients =
        public final RemoteCallbackList<IAccessibilityManagerClient> mClients =
            new RemoteCallbackList<IAccessibilityManagerClient>();
            new RemoteCallbackList<>();


        public final SparseArray<AccessibilityConnectionWrapper> mInteractionConnections =
        public final SparseArray<AccessibilityConnectionWrapper> mInteractionConnections =
                new SparseArray<AccessibilityConnectionWrapper>();
                new SparseArray<>();


        public final SparseArray<IBinder> mWindowTokens = new SparseArray<IBinder>();
        public final SparseArray<IBinder> mWindowTokens = new SparseArray<>();


        // Transient state.
        // Transient state.


        public final CopyOnWriteArrayList<Service> mBoundServices =
        public final CopyOnWriteArrayList<Service> mBoundServices =
                new CopyOnWriteArrayList<Service>();
                new CopyOnWriteArrayList<>();


        public final Map<ComponentName, Service> mComponentNameToServiceMap =
        public final Map<ComponentName, Service> mComponentNameToServiceMap =
                new HashMap<ComponentName, Service>();
                new HashMap<>();


        public final List<AccessibilityServiceInfo> mInstalledServices =
        public final List<AccessibilityServiceInfo> mInstalledServices =
                new ArrayList<AccessibilityServiceInfo>();
                new ArrayList<>();


        public final Set<ComponentName> mBindingServices = new HashSet<ComponentName>();
        public final Set<ComponentName> mBindingServices = new HashSet<>();


        public final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
        public final Set<ComponentName> mEnabledServices = new HashSet<>();


        public final Set<ComponentName> mTouchExplorationGrantedServices =
        public final Set<ComponentName> mTouchExplorationGrantedServices =
                new HashSet<ComponentName>();
                new HashSet<>();


        public int mHandledFeedbackTypes = 0;
        public int mHandledFeedbackTypes = 0;