From a4725efd0bfa52cbddf6ca587d37fc4ebcbfaf72 Mon Sep 17 00:00:00 2001 From: Svetoslav Date: Thu, 24 Jul 2014 14:17:05 -0700 Subject: [PATCH] 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 --- .../AccessibilityService.java | 36 ---- .../IAccessibilityServiceClient.aidl | 2 - .../accessibility/AccessibilityCache.java | 18 +- .../AccessibilityInteractionClient.java | 44 ++--- .../AccessibilityManagerService.java | 175 +++++------------- 5 files changed, 71 insertions(+), 204 deletions(-) diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 19a91a67705fb..13ceb4adfcdd0 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -23,7 +23,6 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; -import android.os.SystemClock; import android.util.Log; import android.view.KeyEvent; import android.view.accessibility.AccessibilityEvent; @@ -659,16 +658,12 @@ public abstract class AccessibilityService extends Service { */ public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub implements HandlerCaller.Callback { - - static final int NO_ID = -1; - private static final int DO_SET_SET_CONNECTION = 1; private static final int DO_ON_INTERRUPT = 2; private static final int DO_ON_ACCESSIBILITY_EVENT = 3; private static final int DO_ON_GESTURE = 4; private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5; private static final int DO_ON_KEY_EVENT = 6; - private static final int DO_ON_WINDOWS_CHANGED = 7; private final HandlerCaller mCaller; @@ -714,12 +709,6 @@ public abstract class AccessibilityService extends Service { mCaller.sendMessage(message); } - @Override - public void onWindowsChanged(int[] windowIds) { - Message message = mCaller.obtainMessageO(DO_ON_WINDOWS_CHANGED, windowIds); - mCaller.sendMessage(message); - } - @Override public void executeMessage(Message message) { switch (message.what) { @@ -791,31 +780,6 @@ public abstract class AccessibilityService extends Service { } } 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 : Log.w(LOG_TAG, "Unknown message type " + message.what); } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index edd872712eb33..6ce0219b81f57 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -39,6 +39,4 @@ import android.view.KeyEvent; void clearAccessibilityCache(); void onKeyEvent(in KeyEvent event, int sequence); - - void onWindowsChanged(in int[] changedWindowIds); } diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java index f796587e8cee0..4f342315504fd 100644 --- a/core/java/android/view/accessibility/AccessibilityCache.java +++ b/core/java/android/view/accessibility/AccessibilityCache.java @@ -58,20 +58,18 @@ final class AccessibilityCache { } } - public void removeWindows(int[] windowIds) { + public void clearWindows() { synchronized (mLock) { - final int windowCount = windowIds.length; - for (int i = 0; i < windowCount; i++) { - final int windowId = windowIds[i]; - AccessibilityWindowInfo window = mWindowCache.get(windowId); + final int windowCount = mWindowCache.size(); + for (int i = windowCount - 1; i >= 0; i--) { + AccessibilityWindowInfo window = mWindowCache.valueAt(i); if (window != null) { if (DEBUG) { - Log.i(LOG_TAG, "Removing window: " + windowId); + Log.i(LOG_TAG, "Removing window: " + window.getId()); } window.recycle(); - mWindowCache.remove(windowId); + mWindowCache.removeAt(i); } - clearNodesForWindowLocked(windowIds[i]); } } } @@ -111,6 +109,10 @@ final class AccessibilityCache { case AccessibilityEvent.TYPE_VIEW_SCROLLED: { clearSubTreeLocked(event.getWindowId(), event.getSourceNodeId()); } break; + + case AccessibilityEvent.TYPE_WINDOWS_CHANGED: { + clearWindows(); + } break; } } diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 4748402f41ffc..db78ec5776c24 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -84,7 +84,7 @@ public final class AccessibilityInteractionClient private static final Object sStaticLock = new Object(); private static final LongSparseArray sClients = - new LongSparseArray(); + new LongSparseArray<>(); private final AtomicInteger mInteractionIdCounter = new AtomicInteger(); @@ -101,7 +101,7 @@ public final class AccessibilityInteractionClient private Message mSameThreadMessage; private static final SparseArray sConnectionCache = - new SparseArray(); + new SparseArray<>(); private static final AccessibilityCache sAccessibilityCache = new AccessibilityCache(); @@ -162,19 +162,6 @@ public final class AccessibilityInteractionClient 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. * @@ -225,11 +212,17 @@ public final class AccessibilityInteractionClient try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { - // The system is just sending data for windows that we introspected - // and changed but not ones that appeared, so we have to always call - // into the system process. This is less expensice as opposed to - // sending all windows on every window change. - List windows = connection.getWindows(); + List windows = sAccessibilityCache.getWindows(); + if (windows != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Windows cache hit"); + } + return windows; + } + if (DEBUG) { + Log.i(LOG_TAG, "Windows cache miss"); + } + windows = connection.getWindows(); if (windows != null) { final int windowCount = windows.size(); for (int i = 0; i < windowCount; i++) { @@ -534,10 +527,6 @@ public final class AccessibilityInteractionClient sAccessibilityCache.onAccessibilityEvent(event); } - public void removeWindows(int[] windowIds) { - sAccessibilityCache.removeWindows(windowIds); - } - /** * 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. final boolean isIpcCall = (Binder.getCallingPid() != Process.myPid()); if (!isIpcCall) { - mFindAccessibilityNodeInfosResult = - new ArrayList(infos); + mFindAccessibilityNodeInfosResult = new ArrayList<>(infos); } else { mFindAccessibilityNodeInfosResult = infos; } @@ -795,8 +783,8 @@ public final class AccessibilityInteractionClient Log.e(LOG_TAG, "No root."); } // Check for duplicates. - HashSet seen = new HashSet(); - Queue fringe = new LinkedList(); + HashSet seen = new HashSet<>(); + Queue fringe = new LinkedList<>(); fringe.add(root); while (!fringe.isEmpty()) { AccessibilityNodeInfo current = fringe.poll(); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 07bb713841758..b1f19540060c1 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -62,7 +62,6 @@ import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; -import android.util.LongArray; import android.util.Pools.Pool; import android.util.Pools.SimplePool; 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 * event dispatch for {@link AccessibilityEvent}s generated across all processes * on the device. Events are dispatched to {@link AccessibilityService}s. - * - * @hide */ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @@ -155,13 +152,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final Object mLock = new Object(); private final Pool mPendingEventPool = - new SimplePool(MAX_POOL_SIZE); + new SimplePool<>(MAX_POOL_SIZE); private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR); private final List mEnabledServicesForFeedbackTempList = - new ArrayList(); + new ArrayList<>(); private final Rect mTempRect = new Rect(); @@ -183,27 +180,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private boolean mHasInputFilter; - private final Set mTempComponentNameSet = new HashSet(); + private final Set mTempComponentNameSet = new HashSet<>(); private final List mTempAccessibilityServiceInfoList = - new ArrayList(); + new ArrayList<>(); private final RemoteCallbackList mGlobalClients = - new RemoteCallbackList(); + new RemoteCallbackList<>(); private final SparseArray mGlobalInteractionConnections = - new SparseArray(); + new SparseArray<>(); - private final SparseArray mGlobalWindowTokens = new SparseArray(); + private final SparseArray mGlobalWindowTokens = new SparseArray<>(); - private final SparseArray mUserStates = new SparseArray(); + private final SparseArray mUserStates = new SparseArray<>(); private final UserManager mUserManager; private int mCurrentUserId = UserHandle.USER_OWNER; - private final LongArray mTempLongArray = new LongArray(); - //TODO: Remove this hack private boolean mInitialized; @@ -439,10 +434,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // to clients as being installed - it really is not. UserState userState = getUserStateLocked(resolvedUserId); if (userState.mUiAutomationService != null) { - List installedServices = - new ArrayList(); + List installedServices = new ArrayList<>(); installedServices.addAll(userState.mInstalledServices); - installedServices.remove(userState.mUiAutomationService); + installedServices.remove(userState.mUiAutomationService.mAccessibilityServiceInfo); return installedServices; } 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. if (userState.mUiAutomationService != null && serviceClient != null - && userState.mUiAutomationService != null && userState.mUiAutomationService.mServiceInterface != null && userState.mUiAutomationService.mServiceInterface.asBinder() == serviceClient.asBinder()) { @@ -907,16 +900,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } - private void notifyWindowsChangedLocked(List 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. * @@ -961,10 +944,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { try { accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext); mTempAccessibilityServiceInfoList.add(accessibilityServiceInfo); - } catch (XmlPullParserException xppe) { + } catch (XmlPullParserException | IOException 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 // as the for loop is running. If that happens, just bail because // there are no more services to notify. - return; } } @@ -1053,7 +1033,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { * Removes a service. * * @param service The service. - * @return True if the service was removed, false otherwise. */ private void removeServiceLocked(Service service, UserState userState) { userState.mBoundServices.remove(service); @@ -1093,7 +1072,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } Set packageNames = service.mPackageNames; - CharSequence packageName = event.getPackageName(); + String packageName = (event.getPackageName() != null) + ? event.getPackageName().toString() : null; if (packageNames.isEmpty() || packageNames.contains(packageName)) { int feedbackType = service.mFeedbackType; @@ -1386,8 +1366,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (mWindowsForAccessibilityCallback != null) { mWindowsForAccessibilityCallback = null; - mWindowManagerService.setWindowsForAccessibilityCallback( - mWindowsForAccessibilityCallback); + mWindowManagerService.setWindowsForAccessibilityCallback(null); } } @@ -1437,8 +1416,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } private boolean readConfigurationForUserStateLocked(UserState userState) { - boolean somthingChanged = false; - somthingChanged |= readAccessibilityEnabledSettingLocked(userState); + boolean somthingChanged = readAccessibilityEnabledSettingLocked(userState); somthingChanged |= readInstalledAccessibilityServiceLocked(userState); somthingChanged |= readEnabledAccessibilityServicesLocked(userState); somthingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState); @@ -1868,7 +1846,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { int mFeedbackType; - Set mPackageNames = new HashSet(); + Set mPackageNames = new HashSet<>(); boolean mIsDefault; @@ -1890,20 +1868,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { boolean mIsAutomation; - final Rect mTempBounds1 = new Rect(); - - final Rect mTempBounds2 = new Rect(); - final ResolveInfo mResolveInfo; // the events pending events to be dispatched to this service final SparseArray mPendingEvents = - new SparseArray(); + new SparseArray<>(); final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher(); final SparseArray mIntrospectedWindows = - new SparseArray(); + new SparseArray<>(); boolean mWasConnectedAndDied; @@ -2106,7 +2080,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (!permissionGranted) { return null; } - List windows = new ArrayList(); + List windows = new ArrayList<>(); final int windowCount = mSecurityPolicy.mWindows.size(); for (int i = 0; i < windowCount; i++) { AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(i); @@ -2646,51 +2620,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } - public void notifyWindowsChangedLocked(List 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) { final IAccessibilityServiceClient listener; 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) { 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_CLEAR_ACCESSIBILITY_CACHE = 3; public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4; - public static final int MSG_ON_WINDOWS_CHANGED = 5; public InvocationHandler(Looper looper) { super(looper, null, true); @@ -2874,11 +2788,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { setOnKeyEventResult(false, eventState.sequence); } break; - case MSG_ON_WINDOWS_CHANGED: { - final int[] windowIds = (int[]) message.obj; - notifyWindowsChangedInternal(windowIds); - } break; - default: { throw new IllegalArgumentException("Unknown message: " + type); } @@ -3028,14 +2937,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public void onWindowsForAccessibilityChanged(List windows) { synchronized (mLock) { - List receivedWindows = (List) windows; - // Populate the windows to report. - List reportedWindows = - new ArrayList(); - final int receivedWindowCount = receivedWindows.size(); + List reportedWindows = new ArrayList<>(); + final int receivedWindowCount = windows.size(); for (int i = 0; i < receivedWindowCount; i++) { - WindowInfo receivedWindow = receivedWindows.get(i); + WindowInfo receivedWindow = windows.get(i); AccessibilityWindowInfo reportedWindow = populateReportedWindow( receivedWindow); if (reportedWindow != null) { @@ -3224,8 +3130,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY; - public final List mWindows = - new ArrayList(); + public final List mWindows = new ArrayList<>(); public int mActiveWindowId = 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_END: 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; } // 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 // source was showing when it was fired, now is the time to send. @@ -3452,10 +3359,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { AccessibilityWindowInfo window = mWindows.get(i); 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) { return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId); } @@ -3568,30 +3483,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // Non-transient state. public final RemoteCallbackList mClients = - new RemoteCallbackList(); + new RemoteCallbackList<>(); public final SparseArray mInteractionConnections = - new SparseArray(); + new SparseArray<>(); - public final SparseArray mWindowTokens = new SparseArray(); + public final SparseArray mWindowTokens = new SparseArray<>(); // Transient state. public final CopyOnWriteArrayList mBoundServices = - new CopyOnWriteArrayList(); + new CopyOnWriteArrayList<>(); public final Map mComponentNameToServiceMap = - new HashMap(); + new HashMap<>(); public final List mInstalledServices = - new ArrayList(); + new ArrayList<>(); - public final Set mBindingServices = new HashSet(); + public final Set mBindingServices = new HashSet<>(); - public final Set mEnabledServices = new HashSet(); + public final Set mEnabledServices = new HashSet<>(); public final Set mTouchExplorationGrantedServices = - new HashSet(); + new HashSet<>(); public int mHandledFeedbackTypes = 0;