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

Commit 31eaf452 authored by Jacky Kao's avatar Jacky Kao Committed by Baligh Uddin
Browse files

Improve the windows register and unregister mechanism

1. For fixing the crash due to mutl-register callback, we avoid using
the display context when un-registering the callback. Instead, we
remove the observer mapping if the display exists along with the
observer of any embedded displays that are children of the
un-registered one.

2. When an display registers its callback first, and then got its
reparent window, there's an displayWindowObserver for it in the
A11yWindowManager. But it didn't be used because it's an embedded
display and any windows change on it will be notified through the
callback of its parent display. So we add a callback to remove
this un-used displayWindowObservers in the A11yWindowManager when
this display is an embedded one.

Bug: 143114102
Test: a11y CTS & unit tests
Merged-In: I00fd1f45c0b4e362143a8d82f31264ff202f0967
(cherry picked from commit 5b8b7377)
parent e59ae90b
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -373,6 +373,20 @@ public class AccessibilityWindowManager {
            }
        }

        /**
         * Called when the display is reparented and becomes an embedded
         * display.
         *
         * @param embeddedDisplayId The embedded display Id.
         */
        @Override
        public void onDisplayReparented(int embeddedDisplayId) {
            // Removes the un-used window observer for the embedded display.
            synchronized (mLock) {
                mDisplayWindowsObservers.remove(embeddedDisplayId);
            }
        }

        private boolean shouldUpdateWindowsLocked(boolean forceSend,
                @NonNull List<WindowInfo> windows) {
            if (forceSend) {
+46 −29
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ import android.view.animation.Interpolator;
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.TraceBuffer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
@@ -172,11 +173,15 @@ final class AccessibilityController {

    /**
     * Sets a callback for observing which windows are touchable for the purposes
     * of accessibility on specified display.
     * of accessibility on specified display. When a display is reparented and becomes
     * an embedded one, the {@link WindowsForAccessibilityCallback#onDisplayReparented(int)}
     * will notify the accessibility framework to remove the un-used window observer of
     * this embedded display.
     *
     * @param displayId The logical display id.
     * @param callback The callback.
     * @return {@code false} if display id is not valid or an embedded display.
     * @return {@code false} if display id is not valid or an embedded display when the callback
     * isn't null.
     */
    boolean setWindowsForAccessibilityCallback(int displayId,
            WindowsForAccessibilityCallback callback) {
@@ -185,12 +190,13 @@ final class AccessibilityController {
                    TAG + ".setWindowsForAccessibilityCallback",
                    "displayId=" + displayId + "; callback={" + callback + "}");
        }

        if (callback != null) {
            final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
            if (dc == null) {
                return false;
            }

        if (callback != null) {
            WindowsForAccessibilityObserver observer =
                    mWindowsForAccessibilityObserver.get(displayId);
            if (isEmbeddedDisplay(dc)) {
@@ -209,21 +215,13 @@ final class AccessibilityController {
                if (Build.IS_DEBUGGABLE) {
                    throw new IllegalStateException(errorMessage);
                }
                removeObserverOfEmbeddedDisplay(observer);
                removeObserversForEmbeddedChildDisplays(observer);
                mWindowsForAccessibilityObserver.remove(displayId);
            }
            observer = new WindowsForAccessibilityObserver(mService, displayId, callback);
            mWindowsForAccessibilityObserver.put(displayId, observer);
            mAllObserversInitialized &= observer.mInitialized;
        } else {
            if (isEmbeddedDisplay(dc)) {
                // If this display is an embedded one, its window observer should be removed along
                // with the window observer of its parent display removed because the window
                // observer of the embedded display and its parent display is the same, and would
                // be removed together when stopping the window tracking of its parent display. So
                // here don't need to do removing window observer of the embedded display again.
                return true;
            }
            final WindowsForAccessibilityObserver windowsForA11yObserver =
                    mWindowsForAccessibilityObserver.get(displayId);
            if (windowsForA11yObserver == null) {
@@ -234,7 +232,7 @@ final class AccessibilityController {
                    throw new IllegalStateException(errorMessage);
                }
            }
            removeObserverOfEmbeddedDisplay(windowsForA11yObserver);
            removeObserversForEmbeddedChildDisplays(windowsForA11yObserver);
            mWindowsForAccessibilityObserver.remove(displayId);
        }
        return true;
@@ -507,24 +505,36 @@ final class AccessibilityController {
        if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) {
            return;
        }
        mService.mH.sendMessage(PooledLambda.obtainMessage(
                AccessibilityController::updateWindowObserverOfEmbeddedDisplay,
                this, embeddedDisplayId, parentWindow));
    }

    private void updateWindowObserverOfEmbeddedDisplay(int embeddedDisplayId,
            WindowState parentWindow) {
        final WindowsForAccessibilityObserver windowsForA11yObserver;

        synchronized (mService.mGlobalLock) {
            // Finds the parent display of this embedded display
        final int parentDisplayId;
            WindowState candidate = parentWindow;
            while (candidate != null) {
                parentWindow = candidate;
                candidate = parentWindow.getDisplayContent().getParentWindow();
            }
        parentDisplayId = parentWindow.getDisplayId();
            final int parentDisplayId = parentWindow.getDisplayId();
            // Uses the observer of parent display
        final WindowsForAccessibilityObserver windowsForA11yObserver =
                mWindowsForAccessibilityObserver.get(parentDisplayId);
            windowsForA11yObserver = mWindowsForAccessibilityObserver.get(parentDisplayId);
        }

        if (windowsForA11yObserver != null) {
            windowsForA11yObserver.notifyDisplayReparented(embeddedDisplayId);
            windowsForA11yObserver.addEmbeddedDisplay(embeddedDisplayId);
            synchronized (mService.mGlobalLock) {
                // Replaces the observer of embedded display to the one of parent display
                mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver);
            }
        }
    }

    void onImeSurfaceShownChanged(WindowState windowState, boolean shown) {
        if (mAccessibilityTracing.isEnabled()) {
@@ -555,7 +565,7 @@ final class AccessibilityController {
                + "mWindowsForAccessibilityObserver=" + mWindowsForAccessibilityObserver);
    }

    private void removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver
    private void removeObserversForEmbeddedChildDisplays(WindowsForAccessibilityObserver
            observerOfParentDisplay) {
        final IntArray embeddedDisplayIdList =
                observerOfParentDisplay.getAndClearEmbeddedDisplayIdList();
@@ -1541,6 +1551,13 @@ final class AccessibilityController {
            mEmbeddedDisplayIdList.add(displayId);
        }

        void notifyDisplayReparented(int embeddedDisplayId) {
            // Notifies the A11y framework the display is reparented and
            // becomes an embedded display for removing the un-used
            // displayWindowObserver of this embedded one.
            mCallback.onDisplayReparented(embeddedDisplayId);
        }

        /**
         * Check if windows have changed, and send them to the accessibility subsystem if they have.
         *
+10 −0
Original line number Diff line number Diff line
@@ -115,6 +115,16 @@ public abstract class WindowManagerInternal {
         */
        void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
                IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows);

        /**
         * Called when the display is reparented and becomes an embedded
         * display. The {@link WindowsForAccessibilityCallback} with the given embedded
         * display will be replaced by the {@link WindowsForAccessibilityCallback}
         * associated with its parent display at the same time.
         *
         * @param embeddedDisplayId The embedded display Id.
         */
        void onDisplayReparented(int embeddedDisplayId);
    }

    /**
+13 −0
Original line number Diff line number Diff line
@@ -834,6 +834,19 @@ public class AccessibilityWindowManagerTest {
        assertNull(token);
    }

    @Test
    public void onDisplayReparented_shouldRemoveObserver() throws RemoteException {
        // Starts tracking window of second display.
        startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
        assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID));
        // Notifies the second display is an embedded one of the default display.
        final WindowsForAccessibilityCallback callbacks =
                mCallbackOfWindows.get(Display.DEFAULT_DISPLAY);
        callbacks.onDisplayReparented(SECONDARY_DISPLAY_ID);
        // Makes sure the observer of the second display is removed.
        assertFalse(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID));
    }

    private void registerLeashedTokenAndWindowId() {
        mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID);
        mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID);