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

Commit df8ad207 authored by Hiroki Sato's avatar Hiroki Sato
Browse files

Exclude unnecessary window early in DisplayWindowsObserver

Previously, some windows are excluded from the final list of
accessibility windows in populateReportedWindowLocked().
This is done after we compute touchable region to decide which windows
are touchable and to be exposed.
Thus, there are some corner cases where an unwanted window joins in
touchable region computation and occludes an important window,
resulting in missing window info.

With this change, unnecessary windows are excluded when we compute
touchable regions.

This is a retry of abandoned CL [1] with a cleaner approach behind
the computeWindowChangesOnA11y flag.

[1] Iacff5b7e67a9e7e989671dc85ad884b0e9f2f2f0

Bug: 240885392
Bug: 254581919
Test: android.autofillservice.cts.servicebehavior.SettingsIntentTest
Test: android.autofillservice.cts.inline.InlineTooltipTest
Test: CtsAccessibilityServiceTestCases CtsAccessibilityTestCases AccessibilityWindowManagerWithAccessibilityWindowTest
Change-Id: Id4b24284db40feaa744c9a5598d0067566fa3963
parent 959ba882
Loading
Loading
Loading
Loading
+27 −13
Original line number Original line Diff line number Diff line
@@ -527,14 +527,19 @@ public class AccessibilityWindowManager {
            final Region unaccountedSpace = new Region(0, 0, screenSize.x, screenSize.y);
            final Region unaccountedSpace = new Region(0, 0, screenSize.x, screenSize.y);
            for (final AccessibilityWindow a11yWindow : visibleWindows) {
            for (final AccessibilityWindow a11yWindow : visibleWindows) {
                a11yWindow.getTouchableRegionInWindow(regionInWindow);
                a11yWindow.getTouchableRegionInWindow(regionInWindow);
                if (windowMattersToAccessibility(a11yWindow, regionInWindow, unaccountedSpace)) {

                final WindowInfo window = a11yWindow.getWindowInfo();
                final WindowInfo window = a11yWindow.getWindowInfo();
                    if (window.token != null) {
                final int windowId = window.token != null
                        ? findWindowIdLocked(userId, window.token)
                        : AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;

                if (windowMattersToAccessibilityLocked(a11yWindow, windowId, regionInWindow,
                        unaccountedSpace)) {
                    if (windowId >= 0) {
                        // Even if token is null, the window will be used in calculating visible
                        // Even if token is null, the window will be used in calculating visible
                        // windows, but is excluded from the accessibility window list.
                        // windows, but is excluded from the accessibility window list.
                        window.regionInScreen.set(regionInWindow);
                        window.regionInScreen.set(regionInWindow);
                        window.layer = addedWindows.size();
                        window.layer = addedWindows.size();
                        final int windowId = findWindowIdLocked(userId, window.token);
                        updateWindowWithWindowAttributes(window, mWindowAttributes.get(windowId));
                        updateWindowWithWindowAttributes(window, mWindowAttributes.get(windowId));


                        windows.add(window);
                        windows.add(window);
@@ -585,8 +590,8 @@ public class AccessibilityWindowManager {
            return windows;
            return windows;
        }
        }


        private static boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow,
        private boolean windowMattersToAccessibilityLocked(AccessibilityWindow a11yWindow,
                Region regionInScreen, Region unaccountedSpace) {
                int windowId, Region regionInScreen, Region unaccountedSpace) {
            if (a11yWindow.ignoreRecentsAnimationForAccessibility()) {
            if (a11yWindow.ignoreRecentsAnimationForAccessibility()) {
                return false;
                return false;
            }
            }
@@ -603,6 +608,10 @@ public class AccessibilityWindowManager {
                return false;
                return false;
            }
            }


            if (isEmbeddedHierarchyWindowsLocked(windowId)) {
                return false;
            }

            // If the window is completely covered by other windows - ignore.
            // If the window is completely covered by other windows - ignore.
            if (unaccountedSpace.quickReject(regionInScreen)) {
            if (unaccountedSpace.quickReject(regionInScreen)) {
                return false;
                return false;
@@ -983,14 +992,19 @@ public class AccessibilityWindowManager {
        private AccessibilityWindowInfo populateReportedWindowLocked(int userId,
        private AccessibilityWindowInfo populateReportedWindowLocked(int userId,
                WindowInfo window, SparseArray<AccessibilityWindowInfo> oldWindowsById) {
                WindowInfo window, SparseArray<AccessibilityWindowInfo> oldWindowsById) {
            final int windowId = findWindowIdLocked(userId, window.token);
            final int windowId = findWindowIdLocked(userId, window.token);

            // With the flag enabled, createWindowInfoListLocked() already removes invalid windows.
            if (!Flags.computeWindowChangesOnA11y()) {
                if (windowId < 0) {
                if (windowId < 0) {
                    return null;
                    return null;
                }
                }


            // Don't need to add the embedded hierarchy windows into the accessibility windows list.
                // Don't need to add the embedded hierarchy windows into the a11y windows list.
                if (isEmbeddedHierarchyWindowsLocked(windowId)) {
                if (isEmbeddedHierarchyWindowsLocked(windowId)) {
                    return null;
                    return null;
                }
                }
            }

            final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();
            final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();


            reportedWindow.setId(windowId);
            reportedWindow.setId(windowId);
@@ -1609,7 +1623,7 @@ public class AccessibilityWindowManager {
                return getWindowTokensForUserLocked(userId).keyAt(userIndex);
                return getWindowTokensForUserLocked(userId).keyAt(userIndex);
            }
            }
        }
        }
        return -1;
        return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
    }
    }


    /**
    /**
+36 −0
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ import static org.mockito.Mockito.when;


import android.annotation.Nullable;
import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Region;
import android.os.IBinder;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.LocaleList;
@@ -433,6 +434,41 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
        assertThat(a11yWindows.get(1), windowId(focusedWindowId));
        assertThat(a11yWindows.get(1), windowId(focusedWindowId));
    }
    }


    @Test
    public void onWindowsChanged_embeddedWindows_shouldOnlyReportHost() throws RemoteException {
        final Rect embeddingBounds = new Rect(0, 0, 200, 100);

        // The embedded window comes front of the host window.
        final IBinder embeddedWindowLeashToken = Mockito.mock(IBinder.class);
        final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
                false, embeddedWindowLeashToken, USER_SYSTEM_ID);
        final AccessibilityWindow embeddedWindow = createMockAccessibilityWindow(
                mA11yWindowTokens.get(embeddedWindowId), Display.DEFAULT_DISPLAY);
        setRegionForMockAccessibilityWindow(embeddedWindow, new Region(embeddingBounds));
        mWindows.get(Display.DEFAULT_DISPLAY).set(0, embeddedWindow);

        final IBinder hostWindowLeashToken = Mockito.mock(IBinder.class);
        final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
                false, hostWindowLeashToken, USER_SYSTEM_ID);
        final AccessibilityWindow hostWindow = createMockAccessibilityWindow(
                mA11yWindowTokens.get(hostWindowId), Display.DEFAULT_DISPLAY);
        setRegionForMockAccessibilityWindow(hostWindow, new Region(embeddingBounds));
        mWindows.get(Display.DEFAULT_DISPLAY).set(1, hostWindow);

        mA11yWindowManager.associateEmbeddedHierarchyLocked(
                hostWindowLeashToken, embeddedWindowLeashToken);

        onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);

        final List<AccessibilityWindowInfo> a11yWindows =
                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
        assertThat(a11yWindows, not(hasItem(windowId(embeddedWindowId))));
        assertThat(a11yWindows.get(0), windowId(hostWindowId));
        final Rect bounds = new Rect();
        a11yWindows.get(0).getBoundsInScreen(bounds);
        assertEquals(bounds, embeddingBounds);
    }

    @Test
    @Test
    public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
    public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
        assertNotEquals("new title",
        assertNotEquals("new title",