Loading services/core/java/com/android/server/wm/ActivityRecord.java +23 −0 Original line number Diff line number Diff line Loading @@ -655,6 +655,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** Whether the IME is showing when transitioning away from this activity. */ boolean mLastImeShown; /** * When set to true, the IME insets will be frozen until the next app becomes IME input target. * @see InsetsPolicy#adjustVisibilityForIme */ boolean mImeInsetsFrozenUntilStartInput; /** * A flag to determine if this AR is in the process of closing or entering PIP. This is needed * to help AR know that the app is in the process of closing but hasn't yet started closing on Loading Loading @@ -1363,6 +1369,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (newTask != null && isState(RESUMED)) { newTask.setResumedActivity(this, "onParentChanged"); mImeInsetsFrozenUntilStartInput = false; } if (rootTask != null && rootTask.topRunningActivity() == this) { Loading Loading @@ -4759,6 +4766,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A && imeInputTarget.getWindow().mActivityRecord == this && mDisplayContent.mInputMethodWindow != null && mDisplayContent.mInputMethodWindow.isVisible(); mImeInsetsFrozenUntilStartInput = true; } final DisplayContent displayContent = getDisplayContent(); Loading Loading @@ -5877,6 +5885,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // closing activity having to wait until idle timeout to be stopped or destroyed if the // next activity won't report idle (e.g. repeated view animation). mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); // If the activity is visible, but no windows are eligible to start input, unfreeze // to avoid permanently frozen IME insets. if (mImeInsetsFrozenUntilStartInput && getWindow( win -> WindowManager.LayoutParams.mayUseInputMethod(win.mAttrs.flags)) == null) { mImeInsetsFrozenUntilStartInput = false; } } } Loading Loading @@ -7792,6 +7808,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } @Override void onResize() { // Reset freezing IME insets flag when the activity resized. mImeInsetsFrozenUntilStartInput = false; super.onResize(); } /** Returns true if the configuration is compatible with this activity. */ boolean isConfigurationCompatible(Configuration config) { final int orientation = getRequestedOrientation(); Loading services/core/java/com/android/server/wm/DisplayContent.java +3 −0 Original line number Diff line number Diff line Loading @@ -3971,6 +3971,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void updateImeInputAndControlTarget(WindowState target) { if (mImeInputTarget != target) { ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target); if (target != null && target.mActivityRecord != null) { target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false; } setImeInputTarget(target); updateImeControlTarget(); } Loading services/core/java/com/android/server/wm/InsetsPolicy.java +31 −10 Original line number Diff line number Diff line Loading @@ -211,7 +211,7 @@ class InsetsPolicy { InsetsState getInsetsForWindow(WindowState target) { final InsetsState originalState = mStateController.getInsetsForWindow(target); final InsetsState state = adjustVisibilityForTransientTypes(originalState); return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state; return adjustVisibilityForIme(target, state, state == originalState); } /** Loading Loading @@ -241,17 +241,38 @@ class InsetsPolicy { return state; } // Navigation bar insets is always visible to IME. private static InsetsState adjustVisibilityForIme(InsetsState originalState, private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState, boolean copyState) { if (w.mIsImWindow) { // Navigation bar insets is always visible to IME. final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); if (originalNavSource != null && !originalNavSource.isVisible()) { final InsetsState state = copyState ? new InsetsState(originalState) : originalState; final InsetsState state = copyState ? new InsetsState(originalState) : originalState; final InsetsSource navSource = new InsetsSource(originalNavSource); navSource.setVisible(true); state.addSource(navSource); return state; } } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) { // During switching tasks with gestural navigation, if the IME is attached to // one app window on that time, even the next app window is behind the IME window, // conceptually the window should not receive the IME insets if the next window is // not eligible IME requester and ready to show IME on top of it. final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp(); final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME); if (shouldImeAttachedToApp && originalImeSource != null) { final boolean imeVisibility = w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME); final InsetsState state = copyState ? new InsetsState(originalState) : originalState; final InsetsSource imeSource = new InsetsSource(originalImeSource); imeSource.setVisible(imeVisibility); state.addSource(imeSource); return state; } } return originalState; } Loading services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +68 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import static android.os.Process.NOBODY_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; Loading Loading @@ -2816,6 +2817,73 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); } @Test public void testImeInsetsFrozenFlag_resetWhenReparented() { final ActivityRecord activity = createActivityWithTask(); final WindowState app = createWindow(null, TYPE_APPLICATION, activity, "app"); final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); final Task newTask = new TaskBuilder(mSupervisor).build(); makeWindowVisible(app, imeWindow); mDisplayContent.mInputMethodWindow = imeWindow; mDisplayContent.setImeLayeringTarget(app); mDisplayContent.setImeInputTarget(app); // Simulate app is closing and expect the last IME is shown and IME insets is frozen. app.mActivityRecord.commitVisibility(false, false); assertTrue(app.mActivityRecord.mLastImeShown); assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); // Expect IME insets frozen state will reset when the activity is reparent to the new task. activity.setState(RESUMED, "test"); activity.reparent(newTask, 0 /* top */, "test"); assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); } @UseTestDisplay(addWindows = W_INPUT_METHOD) @Test public void testImeInsetsFrozenFlag_resetWhenResized() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); makeWindowVisibleAndDrawn(app, mImeWindow); mDisplayContent.setImeLayeringTarget(app); mDisplayContent.setImeInputTarget(app); // Simulate app is closing and expect the last IME is shown and IME insets is frozen. app.mActivityRecord.commitVisibility(false, false); assertTrue(app.mActivityRecord.mLastImeShown); assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); // Expect IME insets frozen state will reset when the activity is reparent to the new task. app.mActivityRecord.onResize(); assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); } @UseTestDisplay(addWindows = W_INPUT_METHOD) @Test public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); makeWindowVisibleAndDrawn(app, mImeWindow); mDisplayContent.setImeLayeringTarget(app); mDisplayContent.setImeInputTarget(app); // Simulate app is closing and expect the last IME is shown and IME insets is frozen. app.mActivityRecord.commitVisibility(false, false); app.mActivityRecord.onWindowsGone(); assertTrue(app.mActivityRecord.mLastImeShown); assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); // Expect IME insets frozen state will reset when the activity has no IME focusable window. app.mActivityRecord.forAllWindowsUnchecked(w -> { w.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; return true; }, true); app.mActivityRecord.commitVisibility(true, false); app.mActivityRecord.onWindowsVisible(); assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); } private void assertHasStartingWindow(ActivityRecord atoken) { assertNotNull(atoken.mStartingSurface); assertNotNull(atoken.mStartingData); Loading services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +1 −0 Original line number Diff line number Diff line Loading @@ -258,6 +258,7 @@ public class SystemServicesTestRule implements TestRule { final ActivityManagerInternal amInternal = mAmService.mInternal; spyOn(amInternal); doNothing().when(amInternal).trimApplications(); doNothing().when(amInternal).scheduleAppGcs(); doNothing().when(amInternal).updateCpuStats(); doNothing().when(amInternal).updateOomAdj(); doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean()); Loading Loading
services/core/java/com/android/server/wm/ActivityRecord.java +23 −0 Original line number Diff line number Diff line Loading @@ -655,6 +655,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** Whether the IME is showing when transitioning away from this activity. */ boolean mLastImeShown; /** * When set to true, the IME insets will be frozen until the next app becomes IME input target. * @see InsetsPolicy#adjustVisibilityForIme */ boolean mImeInsetsFrozenUntilStartInput; /** * A flag to determine if this AR is in the process of closing or entering PIP. This is needed * to help AR know that the app is in the process of closing but hasn't yet started closing on Loading Loading @@ -1363,6 +1369,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (newTask != null && isState(RESUMED)) { newTask.setResumedActivity(this, "onParentChanged"); mImeInsetsFrozenUntilStartInput = false; } if (rootTask != null && rootTask.topRunningActivity() == this) { Loading Loading @@ -4759,6 +4766,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A && imeInputTarget.getWindow().mActivityRecord == this && mDisplayContent.mInputMethodWindow != null && mDisplayContent.mInputMethodWindow.isVisible(); mImeInsetsFrozenUntilStartInput = true; } final DisplayContent displayContent = getDisplayContent(); Loading Loading @@ -5877,6 +5885,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // closing activity having to wait until idle timeout to be stopped or destroyed if the // next activity won't report idle (e.g. repeated view animation). mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); // If the activity is visible, but no windows are eligible to start input, unfreeze // to avoid permanently frozen IME insets. if (mImeInsetsFrozenUntilStartInput && getWindow( win -> WindowManager.LayoutParams.mayUseInputMethod(win.mAttrs.flags)) == null) { mImeInsetsFrozenUntilStartInput = false; } } } Loading Loading @@ -7792,6 +7808,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } @Override void onResize() { // Reset freezing IME insets flag when the activity resized. mImeInsetsFrozenUntilStartInput = false; super.onResize(); } /** Returns true if the configuration is compatible with this activity. */ boolean isConfigurationCompatible(Configuration config) { final int orientation = getRequestedOrientation(); Loading
services/core/java/com/android/server/wm/DisplayContent.java +3 −0 Original line number Diff line number Diff line Loading @@ -3971,6 +3971,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void updateImeInputAndControlTarget(WindowState target) { if (mImeInputTarget != target) { ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target); if (target != null && target.mActivityRecord != null) { target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false; } setImeInputTarget(target); updateImeControlTarget(); } Loading
services/core/java/com/android/server/wm/InsetsPolicy.java +31 −10 Original line number Diff line number Diff line Loading @@ -211,7 +211,7 @@ class InsetsPolicy { InsetsState getInsetsForWindow(WindowState target) { final InsetsState originalState = mStateController.getInsetsForWindow(target); final InsetsState state = adjustVisibilityForTransientTypes(originalState); return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state; return adjustVisibilityForIme(target, state, state == originalState); } /** Loading Loading @@ -241,17 +241,38 @@ class InsetsPolicy { return state; } // Navigation bar insets is always visible to IME. private static InsetsState adjustVisibilityForIme(InsetsState originalState, private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState, boolean copyState) { if (w.mIsImWindow) { // Navigation bar insets is always visible to IME. final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); if (originalNavSource != null && !originalNavSource.isVisible()) { final InsetsState state = copyState ? new InsetsState(originalState) : originalState; final InsetsState state = copyState ? new InsetsState(originalState) : originalState; final InsetsSource navSource = new InsetsSource(originalNavSource); navSource.setVisible(true); state.addSource(navSource); return state; } } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) { // During switching tasks with gestural navigation, if the IME is attached to // one app window on that time, even the next app window is behind the IME window, // conceptually the window should not receive the IME insets if the next window is // not eligible IME requester and ready to show IME on top of it. final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp(); final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME); if (shouldImeAttachedToApp && originalImeSource != null) { final boolean imeVisibility = w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME); final InsetsState state = copyState ? new InsetsState(originalState) : originalState; final InsetsSource imeSource = new InsetsSource(originalImeSource); imeSource.setVisible(imeVisibility); state.addSource(imeSource); return state; } } return originalState; } Loading
services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +68 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import static android.os.Process.NOBODY_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; Loading Loading @@ -2816,6 +2817,73 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); } @Test public void testImeInsetsFrozenFlag_resetWhenReparented() { final ActivityRecord activity = createActivityWithTask(); final WindowState app = createWindow(null, TYPE_APPLICATION, activity, "app"); final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); final Task newTask = new TaskBuilder(mSupervisor).build(); makeWindowVisible(app, imeWindow); mDisplayContent.mInputMethodWindow = imeWindow; mDisplayContent.setImeLayeringTarget(app); mDisplayContent.setImeInputTarget(app); // Simulate app is closing and expect the last IME is shown and IME insets is frozen. app.mActivityRecord.commitVisibility(false, false); assertTrue(app.mActivityRecord.mLastImeShown); assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); // Expect IME insets frozen state will reset when the activity is reparent to the new task. activity.setState(RESUMED, "test"); activity.reparent(newTask, 0 /* top */, "test"); assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); } @UseTestDisplay(addWindows = W_INPUT_METHOD) @Test public void testImeInsetsFrozenFlag_resetWhenResized() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); makeWindowVisibleAndDrawn(app, mImeWindow); mDisplayContent.setImeLayeringTarget(app); mDisplayContent.setImeInputTarget(app); // Simulate app is closing and expect the last IME is shown and IME insets is frozen. app.mActivityRecord.commitVisibility(false, false); assertTrue(app.mActivityRecord.mLastImeShown); assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); // Expect IME insets frozen state will reset when the activity is reparent to the new task. app.mActivityRecord.onResize(); assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); } @UseTestDisplay(addWindows = W_INPUT_METHOD) @Test public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); makeWindowVisibleAndDrawn(app, mImeWindow); mDisplayContent.setImeLayeringTarget(app); mDisplayContent.setImeInputTarget(app); // Simulate app is closing and expect the last IME is shown and IME insets is frozen. app.mActivityRecord.commitVisibility(false, false); app.mActivityRecord.onWindowsGone(); assertTrue(app.mActivityRecord.mLastImeShown); assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); // Expect IME insets frozen state will reset when the activity has no IME focusable window. app.mActivityRecord.forAllWindowsUnchecked(w -> { w.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; return true; }, true); app.mActivityRecord.commitVisibility(true, false); app.mActivityRecord.onWindowsVisible(); assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); } private void assertHasStartingWindow(ActivityRecord atoken) { assertNotNull(atoken.mStartingSurface); assertNotNull(atoken.mStartingData); Loading
services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +1 −0 Original line number Diff line number Diff line Loading @@ -258,6 +258,7 @@ public class SystemServicesTestRule implements TestRule { final ActivityManagerInternal amInternal = mAmService.mInternal; spyOn(amInternal); doNothing().when(amInternal).trimApplications(); doNothing().when(amInternal).scheduleAppGcs(); doNothing().when(amInternal).updateCpuStats(); doNothing().when(amInternal).updateOomAdj(); doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean()); Loading