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

Commit e6510baf authored by Jacky Kao's avatar Jacky Kao Committed by Gerrit Code Review
Browse files

Merge "Eliminate potential deadlock in AccessibilityCache"

parents e19a7da4 fed928bb
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();