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

Commit 0d21e173 authored by Jacky Kao's avatar Jacky Kao Committed by Automerger Merge Worker
Browse files

Merge "Eliminate potential deadlock in AccessibilityCache" am: e6510baf

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1736248

Change-Id: Ic2c767c140ba4961caa63c574e805e70a69f3e63
parents 49082c00 e6510baf
Loading
Loading
Loading
Loading
+28 −49
Original line number Diff line number Diff line
@@ -158,6 +158,7 @@ public class AccessibilityCache {
     * @param event An event.
     */
    public void onAccessibilityEvent(AccessibilityEvent event) {
        AccessibilityNodeInfo nodeToRefresh = null;
        synchronized (mLock) {
            if (DEBUG) {
                Log.i(LOG_TAG, "onAccessibilityEvent(" + event + ")");
@@ -166,17 +167,19 @@ public class AccessibilityCache {
            switch (eventType) {
                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
                    if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                        refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus);
                        removeCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus);
                    }
                    mAccessibilityFocus = event.getSourceNodeId();
                    mAccessibilityFocusedWindow = event.getWindowId();
                    refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus);
                    nodeToRefresh = removeCachedNodeLocked(mAccessibilityFocusedWindow,
                            mAccessibilityFocus);
                } break;

                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
                    if (mAccessibilityFocus == event.getSourceNodeId()
                            && mAccessibilityFocusedWindow == event.getWindowId()) {
                        refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus);
                        nodeToRefresh = removeCachedNodeLocked(mAccessibilityFocusedWindow,
                                mAccessibilityFocus);
                        mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
                        mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
                    }
@@ -184,17 +187,18 @@ public class AccessibilityCache {

                case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
                    if (mInputFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                        refreshCachedNodeLocked(event.getWindowId(), mInputFocus);
                        removeCachedNodeLocked(event.getWindowId(), mInputFocus);
                    }
                    mInputFocus = event.getSourceNodeId();
                    refreshCachedNodeLocked(event.getWindowId(), mInputFocus);
                    nodeToRefresh = removeCachedNodeLocked(event.getWindowId(), mInputFocus);
                } break;

                case AccessibilityEvent.TYPE_VIEW_SELECTED:
                case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
                case AccessibilityEvent.TYPE_VIEW_CLICKED:
                case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
                    refreshCachedNodeLocked(event.getWindowId(), event.getSourceNodeId());
                    nodeToRefresh = removeCachedNodeLocked(event.getWindowId(),
                            event.getSourceNodeId());
                } break;

                case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
@@ -205,7 +209,7 @@ public class AccessibilityCache {
                                & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
                            clearSubTreeLocked(windowId, sourceId);
                        } else {
                            refreshCachedNodeLocked(windowId, sourceId);
                            nodeToRefresh = removeCachedNodeLocked(windowId, sourceId);
                        }
                    }
                } break;
@@ -218,8 +222,8 @@ public class AccessibilityCache {
                    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());
                        // content, we won't clear all cache here with clear().
                        clearWindowCacheLocked();
                        break;
                    }
                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
@@ -228,59 +232,34 @@ public class AccessibilityCache {
            }
        }

        if (nodeToRefresh != null) {
            if (DEBUG) {
                Log.i(LOG_TAG, "Refreshing and re-adding cached node.");
            }
            if (mAccessibilityNodeRefresher.refreshNode(nodeToRefresh, true)) {
                add(nodeToRefresh);
            }
        }
        if (CHECK_INTEGRITY) {
            checkIntegrity();
        }
    }

    private void refreshCachedNodeLocked(int windowId, long sourceId) {
    private AccessibilityNodeInfo removeCachedNodeLocked(int windowId, long sourceId) {
        if (DEBUG) {
            Log.i(LOG_TAG, "Refreshing cached node.");
            Log.i(LOG_TAG, "Removing cached node.");
        }

        LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
        if (nodes == null) {
            return;
            return null;
        }
        AccessibilityNodeInfo cachedInfo = nodes.get(sourceId);
        // If the source is not in the cache - nothing to do.
        if (cachedInfo == null) {
            return;
        }
        // The node changed so we will just refresh it right now.
        if (mAccessibilityNodeRefresher.refreshNode(cachedInfo, true)) {
            return;
        }
        // Weird, we could not refresh. Just evict the entire sub-tree.
        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;
            return null;
        }
        nodes.remove(sourceId);
        return cachedInfo;
    }

    /**
@@ -450,7 +429,7 @@ public class AccessibilityCache {
            if (clone.isAccessibilityFocused()) {
                if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
                        && mAccessibilityFocus != sourceId) {
                    refreshCachedNodeLocked(windowId, mAccessibilityFocus);
                    removeCachedNodeLocked(windowId, mAccessibilityFocus);
                }
                mAccessibilityFocus = sourceId;
                mAccessibilityFocusedWindow = windowId;
+6 −11
Original line number Diff line number Diff line
@@ -563,7 +563,7 @@ public class AccessibilityCacheTest {
    }

    @Test
    public void nodeWithA11yFocusWhenAnotherNodeGetsFocus_getsRefreshed() {
    public void nodeWithA11yFocusWhenAnotherNodeGetsFocus_getsRemoved() {
        AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
        nodeInfo.setAccessibilityFocused(true);
        mAccessibilityCache.add(nodeInfo);
@@ -573,7 +573,7 @@ public class AccessibilityCacheTest {
        mAccessibilityCache.onAccessibilityEvent(event);
        event.recycle();
        try {
            verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
            assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, SINGLE_VIEW_ID));
        } finally {
            nodeInfo.recycle();
        }
@@ -614,7 +614,7 @@ public class AccessibilityCacheTest {
    }

    @Test
    public void nodeWithInputFocusWhenAnotherNodeGetsFocus_getsRefreshed() {
    public void nodeWithInputFocusWhenAnotherNodeGetsFocus_getsRemoved() {
        AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
        nodeInfo.setFocused(true);
        mAccessibilityCache.add(nodeInfo);
@@ -624,7 +624,7 @@ public class AccessibilityCacheTest {
        mAccessibilityCache.onAccessibilityEvent(event);
        event.recycle();
        try {
            verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
            assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, SINGLE_VIEW_ID));
        } finally {
            nodeInfo.recycle();
        }
@@ -733,20 +733,15 @@ public class AccessibilityCacheTest {
    }

    @Test
    public void addA11yFocusNodeBeforeFocusClearedEvent_previousA11yFocusNodeGetsRefreshed() {
    public void addA11yFocusNodeBeforeFocusClearedEvent_previousA11yFocusNodeGetsRemoved() {
        AccessibilityNodeInfo nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
        nodeInfo1.setAccessibilityFocused(true);
        mAccessibilityCache.add(nodeInfo1);
        AccessibilityNodeInfo nodeInfo2 = getNodeWithA11yAndWindowId(OTHER_VIEW_ID, WINDOW_ID_1);
        nodeInfo2.setAccessibilityFocused(true);
        mAccessibilityCache.add(nodeInfo2);
        AccessibilityEvent event = AccessibilityEvent.obtain(
                AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
        event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
        mAccessibilityCache.onAccessibilityEvent(event);
        event.recycle();
        try {
            verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo1, true);
            assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, SINGLE_VIEW_ID));
        } finally {
            nodeInfo1.recycle();
            nodeInfo2.recycle();