From 72c8673be3f0204c4f20c72f90c8a3170c43e5c8 Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Wed, 9 Jun 2021 23:08:33 +0800 Subject: [PATCH] Consolidate disabling fixed-rotation when IME may visible on activity CL[1],[2] disables fixed-rotation when the IME may possible to shown on the launching activity for fixing Bug the IME janking issue that seeing both portrait and landscape IME surface. The CLs use ImeSourceProvider#getSource().mVisibleFrame to predict if the IME may visible during activity launching, which is not very predictable to reflect the true IME client visiblity, like when user intentially presses back key to hide keyboard but somehow mVisibleFrame may not always be null. So that the issue will happen when launching the activity from the caller with different orientation, even previously the IME has been hidden, the fixed-rotation won't happen as expected. The reason is the visible insets of visibleFrame is reported from InputMethodService#onComputeInsets that can be overrided by IME developer, so we can't expect the visibleFrame should always null. Another reason is TaskSnapshot#hasImeVisible is not correct because setHasImeVisible uses isDrawn instead of isVisible to reflect the actual IME visiblity when snapshoting the task. To fix this issue, we should remove and fix those misleading checks as the above, and add reliable visiblity checks to see if the IME window is really possible to show on the launching activity. (e.g. mHasSurface, mViewVisibility, softInputMode of the activity window..) [1]: Idfdf129adbfbee6634d8a27aa78da1f631bd213b [2]: I966ab69f260f828b6e96b3479a36467181288504 Fix: 189014002 Bug: 160451808 Test: manual as below steps: 1) launching the app (e.g. Chrome) with focusing IME in portrait mode 2) swiping out to launcher 3) rotate the device in landscape 4) tapping the shortcut to launch the app again 5) see if the app will be launched with normal rotation animation 6) press back key to hide IME 7) swiping out to launcher 8) tapping the shortcut to launch the app again 9) see if the app will be launched with fixed-rotation. Change-Id: Idef12249806e4a6860b8cd190df4f0f8cad13c19 --- .../com/android/server/wm/ActivityRecord.java | 12 +++++++++ .../com/android/server/wm/DisplayContent.java | 25 ++++++++++++++++--- .../server/wm/TaskSnapshotController.java | 3 ++- .../server/wm/TaskSnapshotControllerTest.java | 4 +-- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 660cd37b123cb..c95243e966c6f 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -646,6 +646,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private boolean mLastContainsDismissKeyguardWindow; private boolean mLastContainsTurnScreenOnWindow; + /** Whether the IME is showing when transitioning away from this activity. */ + boolean mLastImeShown; + /** * 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 @@ -4696,6 +4699,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A setClientVisible(visible); } + if (!visible) { + final InsetsControlTarget imeInputTarget = mDisplayContent.getImeTarget( + DisplayContent.IME_TARGET_INPUT); + mLastImeShown = imeInputTarget != null && imeInputTarget.getWindow() != null + && imeInputTarget.getWindow().mActivityRecord == this + && mDisplayContent.mInputMethodWindow != null + && mDisplayContent.mInputMethodWindow.isVisible(); + } + final DisplayContent displayContent = getDisplayContent(); if (!displayContent.mClosingApps.contains(this) && !displayContent.mOpeningApps.contains(this)) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 81992d8934ede..2ef44a032613d 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -63,6 +63,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; @@ -208,6 +210,7 @@ import android.view.Surface.Rotation; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; +import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManager.DisplayImePolicy; @@ -1556,9 +1559,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // to cover the activity configuration change. return false; } - if ((r.mStartingData != null && r.mStartingData.hasImeSurface()) - || (mInsetsStateController.getImeSourceProvider() - .getSource().getVisibleFrame() != null)) { + if (r.attachedToProcess() && mayImeShowOnLaunchingActivity(r)) { // Currently it is unknown that when will IME window be ready. Reject the case to // avoid flickering by showing IME in inconsistent orientation. return false; @@ -1614,6 +1615,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return true; } + /** Returns {@code true} if the IME is possible to show on the launching activity. */ + private boolean mayImeShowOnLaunchingActivity(@NonNull ActivityRecord r) { + final WindowState win = r.findMainWindow(); + if (win == null) { + return false; + } + // See InputMethodManagerService#shouldRestoreImeVisibility that we expecting the IME + // should be hidden when the window set the hidden softInputMode. + final int softInputMode = win.mAttrs.softInputMode; + switch (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { + case SOFT_INPUT_STATE_ALWAYS_HIDDEN: + case SOFT_INPUT_STATE_HIDDEN: + return false; + } + return r.mLastImeShown && mInputMethodWindow != null && mInputMethodWindow.mHasSurface + && mInputMethodWindow.mViewVisibility == View.VISIBLE; + } + /** Returns {@code true} if the top activity is transformed with the new rotation of display. */ boolean hasTopFixedRotationLaunchingApp() { return mFixedRotationLaunchingApp != null diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 9ffb2b1769153..0000f95e7849d 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -443,7 +443,8 @@ class TaskSnapshotController { } else { excludeLayers = new SurfaceControl[0]; } - builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isDrawn()); + builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible()); + final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = SurfaceControl.captureLayersExcluding( task.getSurfaceControl(), mTmpRect, scaleFraction, diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index e9e201327aa46..97afc165624ac 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -214,8 +214,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { spyOn(mDisplayContent); spyOn(mDisplayContent.mInputMethodWindow); when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(true); - // Intentionally set the IME window is in drawn state. - doReturn(true).when(mDisplayContent.mInputMethodWindow).isDrawn(); + // Intentionally set the IME window is in visible state. + doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible(); // Verify no NPE happens when calling createTaskSnapshot. try { final TaskSnapshot.Builder builder = new TaskSnapshot.Builder();