diff options
| author | 2019-12-13 01:27:08 +0000 | |
|---|---|---|
| committer | 2019-12-13 01:27:08 +0000 | |
| commit | 24ca88cf305068a7133e5b879735a585d977a66d (patch) | |
| tree | f0b0f065bbbed9e4b92a157892095a14ce858ed8 | |
| parent | 8c56261c297b4bc32be5364ac075bed435188911 (diff) | |
| parent | 24a1ac51bfd89633798554dc625f1adb181aed34 (diff) | |
Merge "Don't clear cache when changing accessibility focus"
4 files changed, 126 insertions, 14 deletions
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java index dc8bf9b5fbae..9ab2c2b8bcb1 100644 --- a/core/java/android/view/accessibility/AccessibilityCache.java +++ b/core/java/android/view/accessibility/AccessibilityCache.java @@ -69,6 +69,8 @@ public class AccessibilityCache { private long mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; private long mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; + private int mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; + private boolean mIsAllWindowsCached; // The SparseArray of all {@link AccessibilityWindowInfo}s on all displays. @@ -164,16 +166,19 @@ public class AccessibilityCache { switch (eventType) { case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - refreshCachedNodeLocked(event.getWindowId(), mAccessibilityFocus); + refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus); } mAccessibilityFocus = event.getSourceNodeId(); - refreshCachedNodeLocked(event.getWindowId(), mAccessibilityFocus); + mAccessibilityFocusedWindow = event.getWindowId(); + refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus); } break; case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { - if (mAccessibilityFocus == event.getSourceNodeId()) { - refreshCachedNodeLocked(event.getWindowId(), mAccessibilityFocus); + if (mAccessibilityFocus == event.getSourceNodeId() + && mAccessibilityFocusedWindow == event.getWindowId()) { + refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus); mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; + mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; } } break; @@ -210,6 +215,13 @@ public class AccessibilityCache { } break; case AccessibilityEvent.TYPE_WINDOWS_CHANGED: + if (event.getWindowChanges() + == AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED) { + // Don't need to clear all cache. Unless the changes are related to + // content, we won't clear all cache here. + refreshCachedWindowLocked(event.getWindowId()); + break; + } case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: { clear(); } break; @@ -243,6 +255,34 @@ public class AccessibilityCache { clearSubTreeLocked(windowId, sourceId); } + private void refreshCachedWindowLocked(int windowId) { + if (DEBUG) { + Log.i(LOG_TAG, "Refreshing cached window."); + } + + if (windowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) { + return; + } + + final int displayCounts = mWindowCacheByDisplay.size(); + for (int i = 0; i < displayCounts; i++) { + final SparseArray<AccessibilityWindowInfo> windowsOfDisplay = + mWindowCacheByDisplay.valueAt(i); + if (windowsOfDisplay == null) { + continue; + } + final AccessibilityWindowInfo window = windowsOfDisplay.get(windowId); + if (window == null) { + continue; + } + if (!mAccessibilityNodeRefresher.refreshWindow(window)) { + // If we fail to refresh the window, clear all windows. + clearWindowCacheLocked(); + } + return; + } + } + /** * Gets a cached {@link AccessibilityNodeInfo} given the id of the hosting * window and the accessibility id of the node. @@ -413,8 +453,10 @@ public class AccessibilityCache { refreshCachedNodeLocked(windowId, mAccessibilityFocus); } mAccessibilityFocus = sourceId; + mAccessibilityFocusedWindow = windowId; } else if (mAccessibilityFocus == sourceId) { mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; + mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; } if (clone.isFocused()) { mInputFocus = sourceId; @@ -439,6 +481,8 @@ public class AccessibilityCache { mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; + + mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; } } @@ -653,8 +697,14 @@ public class AccessibilityCache { // Layer of indirection included to break dependency chain for testing public static class AccessibilityNodeRefresher { + /** Refresh the given AccessibilityNodeInfo object. */ public boolean refreshNode(AccessibilityNodeInfo info, boolean bypassCache) { return info.refresh(null, bypassCache); } + + /** Refresh the given AccessibilityWindowInfo object. */ + public boolean refreshWindow(AccessibilityWindowInfo info) { + return info.refresh(); + } } } diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index bb10ef10d79e..386651731d45 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -223,19 +223,36 @@ public final class AccessibilityInteractionClient * @return The {@link AccessibilityWindowInfo}. */ public AccessibilityWindowInfo getWindow(int connectionId, int accessibilityWindowId) { + return getWindow(connectionId, accessibilityWindowId, /* bypassCache */ false); + } + + /** + * Gets the info for a window. + * + * @param connectionId The id of a connection for interacting with the system. + * @param accessibilityWindowId A unique window id. Use + * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param bypassCache Whether to bypass the cache. + * @return The {@link AccessibilityWindowInfo}. + */ + public AccessibilityWindowInfo getWindow(int connectionId, int accessibilityWindowId, + boolean bypassCache) { try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { - AccessibilityWindowInfo window = sAccessibilityCache.getWindow( - accessibilityWindowId); - if (window != null) { + AccessibilityWindowInfo window; + if (!bypassCache) { + window = sAccessibilityCache.getWindow(accessibilityWindowId); + if (window != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Window cache hit"); + } + return window; + } if (DEBUG) { - Log.i(LOG_TAG, "Window cache hit"); + Log.i(LOG_TAG, "Window cache miss"); } - return window; - } - if (DEBUG) { - Log.i(LOG_TAG, "Window cache miss"); } final long identityToken = Binder.clearCallingIdentity(); try { @@ -244,7 +261,9 @@ public final class AccessibilityInteractionClient Binder.restoreCallingIdentity(identityToken); } if (window != null) { - sAccessibilityCache.addWindow(window); + if (!bypassCache) { + sAccessibilityCache.addWindow(window); + } return window; } } else { diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java index 2cc6e9aebd74..ca5c417bdc6d 100644 --- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java +++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java @@ -87,6 +87,8 @@ public final class AccessibilityWindowInfo implements Parcelable { /** @hide */ public static final int ACTIVE_WINDOW_ID = Integer.MAX_VALUE; /** @hide */ + public static final int UNDEFINED_CONNECTION_ID = -1; + /** @hide */ public static final int UNDEFINED_WINDOW_ID = -1; /** @hide */ public static final int ANY_WINDOW_ID = -2; @@ -117,7 +119,7 @@ public final class AccessibilityWindowInfo implements Parcelable { private CharSequence mTitle; private long mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID; - private int mConnectionId = UNDEFINED_WINDOW_ID; + private int mConnectionId = UNDEFINED_CONNECTION_ID; /** * Creates a new {@link AccessibilityWindowInfo}. @@ -539,6 +541,30 @@ public final class AccessibilityWindowInfo implements Parcelable { } } + /** + * Refreshes this window with the latest state of the window it represents. + * <p> + * <strong>Note:</strong> If this method returns false this info is obsolete + * since it represents a window that is no longer exist. + * </p> + * + * @hide + */ + public boolean refresh() { + if (mConnectionId == UNDEFINED_CONNECTION_ID || mId == UNDEFINED_WINDOW_ID) { + return false; + } + final AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + final AccessibilityWindowInfo refreshedInfo = client.getWindow(mConnectionId, + mId, /* bypassCache */true); + if (refreshedInfo == null) { + return false; + } + init(refreshedInfo); + refreshedInfo.recycle(); + return true; + } + @Override public int describeContents() { return 0; @@ -586,6 +612,7 @@ public final class AccessibilityWindowInfo implements Parcelable { mTitle = other.mTitle; mAnchorId = other.mAnchorId; + if (mChildIds != null) mChildIds.clear(); if (other.mChildIds != null && other.mChildIds.size() > 0) { if (mChildIds == null) { mChildIds = other.mChildIds.clone(); diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java index 6bce6517a85d..0d5db6d791f1 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java @@ -436,6 +436,22 @@ public class AccessibilityCacheTest { } @Test + public void windowsChangedWithWindowsChangeA11yFocusedEvent_dontClearCache() { + AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); + mAccessibilityCache.add(nodeInfo); + AccessibilityEvent event = new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOWS_CHANGED); + event.setWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED); + mAccessibilityCache.onAccessibilityEvent(event); + AccessibilityNodeInfo cachedNode = mAccessibilityCache.getNode(WINDOW_ID_1, + nodeInfo.getSourceNodeId()); + try { + assertNotNull(cachedNode); + } finally { + nodeInfo.recycle(); + } + } + + @Test public void subTreeChangeEvent_clearsNodeAndChild() { AccessibilityEvent event = AccessibilityEvent .obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); |