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

Commit 449798f3 authored by Cosmin Băieș's avatar Cosmin Băieș
Browse files

Fix IME Overlay Layering Target visibility updates

In [1] we started tracking the visibility of the IME Input Target, and
the IME Overlay Layering target (i.e. IME Layering Target that has
FLAG_NOT_FOCUSABLE and FLAG_ALT_FOCUSABLE_IM).

If there is a visible IME Overlay Layering Target, and the IME Input
Target becomes invisible, then we hide the IME. In this case, the IME
Input Target won't be updated, as the layering target is not focusable.
If we wouldn't explicitly hide, the IME would show on the new layering
target, using the requestedVisibleTypes of the (now outdated) input
target.

However, the initial issue tracked by [1] is still reproducible.
Moreover, this state update is quite brittle, and in many cases left
with an outdated value, which leads to InputMethodManagerService sending
explicit IME hide request, because at some point we had a visible IME
Overlay Layering target, and we didn't reset the state properly.

This will first make sure we update this state properly when needed,
with a follow up CL tracking this state removal, as well as a proper fix
to the initial issue (252192121).

  [1]: Ia71b975898efb19439c3a1b1a9a2bdcf21b78650

Flag: EXEMPT bugfix
Bug: 407125743
Test: atest DisplayContentTests#testDispatchImeOverlayLayeringTargetVisibilityChanged_whenLayeringTargetNull
  DisplayContentTests#testDispatchImeOverlayLayeringTargetVisibilityChanged_whenLayeringTargetChanges
  DisplayContentTests#testDispatchImeOverlayLayeringTargetVisibilityChanged_whenOverlayTargetAdded
Change-Id: I3ddcc49947f68a18216551bc9434ce453debdeea
parent 5e4edde8
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -4402,6 +4402,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        boolean forceUpdateImeParent = target != mImeLayeringTarget;
        mImeLayeringTarget = target;

        if (target != null && target.isImeOverlayLayeringTarget()) {
            mWmService.dispatchImeOverlayLayeringTargetVisibilityChanged(target.mClient.asBinder(),
                    target.mAttrs.type, target.isVisibleRequestedOrAdding() /* visible */,
                    false /* removed */, mDisplayId);
        } else {
            final int windowType = target != null ? target.mAttrs.type : INVALID_WINDOW_TYPE;
            mWmService.dispatchImeOverlayLayeringTargetVisibilityChanged(null /* token */,
                    windowType, false /* visible */, true /* removed */, mDisplayId);
        }

        // 1. Reparent the IME container window to the target root DA to get the correct bounds and
        // config. Only happens when the target window is in a different root DA and ImeContainer
        // is not organized (see FEATURE_IME and updateImeParent).
+1 −6
Original line number Diff line number Diff line
@@ -2042,11 +2042,6 @@ public class WindowManagerService extends IWindowManager.Stub

        if (imMayMove) {
            displayContent.computeImeLayeringTarget(true /* update */);
            if (win.isImeOverlayLayeringTarget()) {
                dispatchImeOverlayLayeringTargetVisibilityChanged(client.asBinder(),
                        win.mAttrs.type, win.isVisibleRequestedOrAdding(), false /* removed */,
                        displayContent.getDisplayId());
            }
        }

        // Don't do layout here, the window must call
@@ -3666,7 +3661,7 @@ public class WindowManagerService extends IWindowManager.Stub
        });
    }

    void dispatchImeOverlayLayeringTargetVisibilityChanged(@NonNull IBinder token,
    void dispatchImeOverlayLayeringTargetVisibilityChanged(@Nullable IBinder token,
            @WindowManager.LayoutParams.WindowType int windowType, boolean visible,
            boolean removed, int displayId) {
        if (DEBUG_INPUT_METHOD) {
+0 −4
Original line number Diff line number Diff line
@@ -2238,10 +2238,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        super.removeImmediately();

        final DisplayContent dc = getDisplayContent();
        if (isImeOverlayLayeringTarget()) {
            mWmService.dispatchImeOverlayLayeringTargetVisibilityChanged(mClient.asBinder(),
                    mAttrs.type, false /* visible */, true /* removed */, dc.getDisplayId());
        }
        if (isImeLayeringTarget()) {
            // Remove the attached IME screenshot.
            dc.removeImeScreenshotByTarget(this);
+94 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -102,6 +103,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
@@ -2388,6 +2390,98 @@ public class DisplayContentTests extends WindowTestsBase {
        assertNull(mDisplayContent.mImeScreenshot);
    }

    @SetupWindows(addWindows = W_INPUT_METHOD)
    @Test
    public void testDispatchImeOverlayLayeringTargetVisibilityChanged_whenLayeringTargetNull() {
        final DisplayContent dc = createNewDisplay();
        final var wmService = dc.mWmService;
        spyOn(wmService);
        final WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).setDisplay(dc)
                .build();
        app.mAttrs.flags |= FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;
        dc.setInputMethodWindowLocked(mImeWindow);

        assertEquals("app became the IME layering target", app,
                dc.getImeLayeringTarget());
        assertTrue("app is an IME overlay layering target", app.isImeOverlayLayeringTarget());
        verify(wmService).dispatchImeOverlayLayeringTargetVisibilityChanged(
                eq(app.mClient.asBinder()), eq(TYPE_BASE_APPLICATION), eq(true) /* visible */,
                eq(false) /* removed */, eq(dc.mDisplayId));

        // Removing IME window updates IME layering target to null
        dc.setInputMethodWindowLocked(null /* win */);
        assertNotEquals("app is no longer the IME layering target after IME was removed",
                app, dc.getImeLayeringTarget());
        assertFalse("app is no longer an IME overlay layering target after IME was removed",
                app.isImeOverlayLayeringTarget());
        verify(wmService).dispatchImeOverlayLayeringTargetVisibilityChanged(isNull() /* token */,
                eq(INVALID_WINDOW_TYPE), eq(false) /* visible */, eq(true) /* removed */,
                eq(dc.mDisplayId));
    }

    @SetupWindows(addWindows = W_INPUT_METHOD)
    @Test
    public void testDispatchImeOverlayLayeringTargetVisibilityChanged_whenLayeringTargetChanges() {
        final DisplayContent dc = createNewDisplay();
        final var wmService = dc.mWmService;
        spyOn(wmService);
        final WindowState app1 = newWindowBuilder("app1", TYPE_BASE_APPLICATION)
                .setDisplay(dc).build();
        final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION)
                .setDisplay(dc).build();
        app1.mAttrs.flags |= FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;
        app2.mActivityRecord.setVisibleRequested(false);
        dc.setInputMethodWindowLocked(mImeWindow);

        assertEquals("app1 became the IME layering target", app1,
                dc.getImeLayeringTarget());
        assertTrue("app1 is an IME overlay layering target", app1.isImeOverlayLayeringTarget());
        verify(wmService).dispatchImeOverlayLayeringTargetVisibilityChanged(
                eq(app1.mClient.asBinder()), eq(TYPE_BASE_APPLICATION), eq(true) /* visible */,
                eq(false) /* removed */, eq(dc.mDisplayId));

        app2.mActivityRecord.setVisibleRequested(true);
        dc.computeImeLayeringTarget(true /* update */);
        assertEquals("app2 became the IME layering target", app2,
                dc.getImeLayeringTarget());
        assertFalse("app2 is not an IME overlay layering target",
                app2.isImeOverlayLayeringTarget());
        verify(wmService).dispatchImeOverlayLayeringTargetVisibilityChanged(isNull() /* token */,
                eq(TYPE_APPLICATION), eq(false) /* visible */, eq(true) /* removed */,
                eq(dc.mDisplayId));
    }

    @SetupWindows(addWindows = W_INPUT_METHOD)
    @Test
    public void testDispatchImeOverlayLayeringTargetVisibilityChanged_whenOverlayTargetAdded() {
        final DisplayContent dc = createNewDisplay();
        final var wmService = dc.mWmService;
        spyOn(wmService);
        final WindowState app1 = newWindowBuilder("app1", TYPE_BASE_APPLICATION)
                .setDisplay(dc).build();
        dc.setInputMethodWindowLocked(mImeWindow);

        assertEquals("app1 became the IME layering target", app1,
                dc.getImeLayeringTarget());
        assertFalse("app1 is not an IME overlay layering target",
                app1.isImeOverlayLayeringTarget());
        verify(wmService, never()).dispatchImeOverlayLayeringTargetVisibilityChanged(
                any(Binder.class), anyInt(), anyBoolean(), anyBoolean(), anyInt());

        final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION)
                .setParent(app1)
                .setDisplay(dc).build();
        app2.mAttrs.flags |= FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;

        dc.computeImeLayeringTarget(true /* update */);
        assertEquals("app2 became the IME layering target", app2,
                dc.getImeLayeringTarget());
        assertTrue("app2 is an IME overlay layering target", app2.isImeOverlayLayeringTarget());
        verify(wmService).dispatchImeOverlayLayeringTargetVisibilityChanged(
                eq(app2.mClient.asBinder()), eq(TYPE_APPLICATION), eq(true) /* visible */,
                eq(false) /* removed */, eq(dc.mDisplayId));
    }

    @Test
    public void testRotateBounds_keepSamePhysicalPosition() {
        final DisplayContent dc =