From a308e124d15e29df8df6468b98d943a6208483fc Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Thu, 24 Nov 2022 12:00:39 +0800 Subject: [PATCH] Fix updateAboveInsetsState may not apply IME window in some cases In case updateAboveInsetsState didn't apply IME window to update app's above insets state when an edge case happens during unlocking the device that WindowState#applyImeWindowsIfNeeded returns false because the input target has not yet being drawn or visible, we should keep applying the IME window to update the input target's insets state when the input target is during unlock and in requested visible state. Bug: 246402296 Bug: 201987724 Test: atest InsetsStateControllerTest#\ testUpdateAboveInsetsState_imeTargetOnScreenBehavior Test: atest RemoteAnimationControllerTest#\ testLaunchRemoteAnimationWithoutImeBehind Test: manual as Bug 246402296 1) Launch a chat app 2) Start a chat channel to show IME 3) Press power button to turn the screen off 4) Press power button to turn the screen on and unlock 5) Expect the chat activity looks fine without flickers Test: manual as Bug 201987724 0-1) Set secure unlock method (e.g. pattern) 0-2) Install "Google Pay" app 0-3) Add a payment method instruction 1) Start Message app 2) Click search window and wait IME window appears 3) Hit power button to lock the device 4) Tap screen to show Lockscreen 5) Tap GPay icon 6) See animation and expect there is no IME flickering behind Change-Id: Ib43f4e2659d3afab63dc327f5f939fbd13afa5f3 --- .../com/android/server/wm/WindowState.java | 3 +- .../server/wm/InsetsStateControllerTest.java | 38 +++++++++++++++++++ .../wm/RemoteAnimationControllerTest.java | 1 + 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 19409b1f3636b..73759d3a3362f 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4927,7 +4927,8 @@ class WindowState extends WindowContainer implements WindowManagerP // animation on the keyguard but seeing the IME window that originally on the app // which behinds the keyguard. final WindowState imeInputTarget = getImeInputTarget(); - if (imeInputTarget != null && !(imeInputTarget.isDrawn() || imeInputTarget.isVisible())) { + if (imeInputTarget != null + && !(imeInputTarget.isDrawn() || imeInputTarget.isVisibleRequested())) { return false; } return mDisplayContent.forAllImeWindows(callback, traverseTopToBottom); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index c898119ea991d..cdb264222a7ec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -442,6 +442,44 @@ public class InsetsStateControllerTest extends WindowTestsBase { verify(navBar, atLeastOnce()).notifyInsetsChanged(); } + @Test + public void testUpdateAboveInsetsState_imeTargetOnScreenBehavior() { + final WindowToken imeToken = createTestWindowToken(TYPE_INPUT_METHOD, mDisplayContent); + final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, imeToken, "ime"); + final WindowState app = createTestWindow("app"); + + getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null); + ime.getControllableInsetProvider().setServerVisible(true); + + app.mActivityRecord.setVisibility(true); + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.updateImeInputAndControlTarget(app); + + app.setRequestedVisibleTypes(ime(), ime()); + getController().onInsetsModified(app); + assertTrue(ime.getControllableInsetProvider().getSource().isVisible()); + + getController().updateAboveInsetsState(true /* notifyInsetsChange */); + assertNotNull(app.getInsetsState().peekSource(ITYPE_IME)); + verify(app, atLeastOnce()).notifyInsetsChanged(); + + // Expect the app will still get IME insets even when the app was invisible. + // (i.e. app invisible after locking the device) + app.mActivityRecord.setVisible(false); + app.setHasSurface(false); + getController().updateAboveInsetsState(true /* notifyInsetsChange */); + assertNotNull(app.getInsetsState().peekSource(ITYPE_IME)); + verify(app, atLeastOnce()).notifyInsetsChanged(); + + // Expect the app will get IME insets when the app is requesting visible. + // (i.e. app is going to visible when unlocking the device) + app.mActivityRecord.setVisibility(true); + assertTrue(app.isVisibleRequested()); + getController().updateAboveInsetsState(true /* notifyInsetsChange */); + assertNotNull(app.getInsetsState().peekSource(ITYPE_IME)); + verify(app, atLeastOnce()).notifyInsetsChanged(); + } + @Test public void testDispatchGlobalInsets() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar"); diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index c548dc3aebd56..eb26415c2b213 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -772,6 +772,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { // Simulating now win1 is being covered by the lockscreen which has no surface, // and then launching an activity win2 with the remote animation win1.mHasSurface = false; + win1.mActivityRecord.setVisibility(false); mDisplayContent.mOpeningApps.add(win2.mActivityRecord); final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win2.mActivityRecord, new Point(50, 100), null,