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

Commit 224c55d4 authored by Ryan Lin's avatar Ryan Lin Committed by Android (Google) Code Review
Browse files

Merge "Fix the race condition that cause windows cache stale"

parents 5caf17ce e169358d
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ package android.view.accessibility;
import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY;

import android.os.Build;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.Log;
import android.util.LongArray;
@@ -71,6 +72,11 @@ public class AccessibilityCache {

    private long mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
    private long mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
    /**
     * The event time of the {@link AccessibilityEvent} which presents the populated windows cache
     * before it is stale.
     */
    private long mValidWindowCacheTimeStamp = 0;

    private int mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
    private int mInputFocusWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
@@ -97,13 +103,20 @@ public class AccessibilityCache {
     * The key of SparseArray is display ID.
     *
     * @param windowsOnAllDisplays The accessibility windows of all displays.
     * @param populationTimeStamp The timestamp from {@link SystemClock#uptimeMillis()} when the
     *                            client requests the data.
     */
    public void setWindowsOnAllDisplays(
            SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays) {
            SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays,
            long populationTimeStamp) {
        synchronized (mLock) {
            if (DEBUG) {
                Log.i(LOG_TAG, "Set windows");
            }
            if (mValidWindowCacheTimeStamp > populationTimeStamp) {
                // Discard the windows because it might be stale.
                return;
            }
            clearWindowCacheLocked();
            if (windowsOnAllDisplays == null) {
                return;
@@ -224,6 +237,7 @@ public class AccessibilityCache {
                } break;

                case AccessibilityEvent.TYPE_WINDOWS_CHANGED:
                    mValidWindowCacheTimeStamp = event.getEventTime();
                    if (event.getWindowChanges()
                            == AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED) {
                        // Don't need to clear all cache. Unless the changes are related to
@@ -232,6 +246,7 @@ public class AccessibilityCache {
                        break;
                    }
                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
                    mValidWindowCacheTimeStamp = event.getEventTime();
                    clear();
                } break;
            }
+3 −1
Original line number Diff line number Diff line
@@ -435,8 +435,10 @@ public final class AccessibilityInteractionClient
                    }
                }

                long populationTimeStamp;
                final long identityToken = Binder.clearCallingIdentity();
                try {
                    populationTimeStamp = SystemClock.uptimeMillis();
                    windows = connection.getWindows();
                } finally {
                    Binder.restoreCallingIdentity(identityToken);
@@ -446,7 +448,7 @@ public final class AccessibilityInteractionClient
                }
                if (windows != null) {
                    if (sAccessibilityCache != null) {
                        sAccessibilityCache.setWindowsOnAllDisplays(windows);
                        sAccessibilityCache.setWindowsOnAllDisplays(windows, populationTimeStamp);
                    }
                    return windows;
                }
+49 −2
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.os.SystemClock;
import android.util.SparseArray;
import android.view.Display;
import android.view.View;
@@ -299,7 +300,8 @@ public class AccessibilityCacheTest {
            SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>();
            allWindows.put(Display.DEFAULT_DISPLAY, windowsIn1);
            allWindows.put(SECONDARY_DISPLAY_ID, windowsIn2);
            mAccessibilityCache.setWindowsOnAllDisplays(allWindows);
            final long populationTimeStamp = SystemClock.uptimeMillis();
            mAccessibilityCache.setWindowsOnAllDisplays(allWindows, populationTimeStamp);
            // Gets windows at default display.
            windowsOut1 = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
            window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
@@ -338,6 +340,46 @@ public class AccessibilityCacheTest {
        }
    }

    @Test
    public void setInvalidWindowsAfterWindowsChangedEvent_notInCache() {
        final AccessibilityEvent event = new AccessibilityEvent(
                AccessibilityEvent.TYPE_WINDOWS_CHANGED);
        final long eventTime = 1000L;
        event.setEventTime(eventTime);
        mAccessibilityCache.onAccessibilityEvent(event);

        final AccessibilityWindowInfo windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1,
                SPECIFIC_WINDOW_LAYER);
        List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1);
        setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn, eventTime - 10);

        try {
            assertNull(getWindowsByDisplay(Display.DEFAULT_DISPLAY));
        } finally {
            windowInfo1.recycle();
        }
    }

    @Test
    public void setInvalidWindowsAfterStateChangedEvent_notInCache() {
        final AccessibilityEvent event = new AccessibilityEvent(
                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
        final long eventTime = 1000L;
        event.setEventTime(eventTime);
        mAccessibilityCache.onAccessibilityEvent(event);

        final AccessibilityWindowInfo windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1,
                SPECIFIC_WINDOW_LAYER);
        List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1);
        setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn, eventTime - 10);

        try {
            assertNull(getWindowsByDisplay(Display.DEFAULT_DISPLAY));
        } finally {
            windowInfo1.recycle();
        }
    }

    @Test
    public void addWindowThenStateChangedEvent_noLongerInCache() {
        putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
@@ -1063,9 +1105,14 @@ public class AccessibilityCacheTest {
    }

    private void setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows) {
        setWindowsByDisplay(displayId, windows, SystemClock.uptimeMillis());
    }

    private void setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows,
            long populationTimeStamp) {
        SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>();
        allWindows.put(displayId, windows);
        mAccessibilityCache.setWindowsOnAllDisplays(allWindows);
        mAccessibilityCache.setWindowsOnAllDisplays(allWindows, populationTimeStamp);
    }

    private List<AccessibilityWindowInfo> getWindowsByDisplay(int displayId) {