From 34a280bc45a7afcf6c8bfd9d416ada4c8309f813 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 12 May 2022 23:53:52 +0000 Subject: [PATCH 1/2] Ensure recents input consumer can be focused in Overview - Previously, when a fix for b/138622418 was introduced, it also changed the behavior to make the windows being animated by recents not-touchable, which caused additional checks on the input monitor side to handle this case. But because we already have the recents input consumer in place to intercept input, we can leave the window touchability as is (as we do in other cases like PIP where we also intercept all input), and rely on the existing behavior in input monitor to focus the consumer if one of its animating windows is focused. To not regress on b/138622418, we push the check up to where the window is considered for affecting the exclusion rects instead. This enabled the followup CL of routing the back gesture to the consumer with the new back flow. Bug: 223750399 Test: Verify b/138622418 - swipe up from home and quickly swipe to -1 and ensure back doesn't show Test: Verify b/191058092 - receive a HUN while in overview and ensure input focus switches from recents input consumer -> notif shade and IME shows, and then back to the input consumer when finished Test: Verify b/177923822 - recents input consumer is always focused in overview in all nav modes and volume works and is routed to launcher to handle Change-Id: I79160004afc7d6759563ef68a9e2a480ef3d900a --- .../java/com/android/server/wm/DisplayContent.java | 7 ++++++- .../java/com/android/server/wm/InputMonitor.java | 11 +++++------ .../java/com/android/server/wm/WindowState.java | 14 +------------- .../com/android/server/wm/WindowStateTests.java | 11 ----------- 4 files changed, 12 insertions(+), 31 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c12f7f33b069c..d413dcd3442bd 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5440,13 +5440,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final Region local = Region.obtain(); final int[] remainingLeftRight = {mSystemGestureExclusionLimit, mSystemGestureExclusionLimit}; + final RecentsAnimationController recentsAnimationController = + mWmService.getRecentsAnimationController(); // Traverse all windows top down to assemble the gesture exclusion rects. // For each window, we only take the rects that fall within its touchable region. forAllWindows(w -> { + final boolean ignoreRecentsAnimationTarget = recentsAnimationController != null + && recentsAnimationController.shouldApplyInputConsumer(w.getActivityRecord()); if (!w.canReceiveTouchInput() || !w.isVisible() || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0 - || unhandled.isEmpty()) { + || unhandled.isEmpty() + || ignoreRecentsAnimationTarget) { return; } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index ea72e12783c36..27a4fc10da5b6 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -545,12 +545,7 @@ final class InputMonitor { @Override public void accept(WindowState w) { final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle; - final RecentsAnimationController recentsAnimationController = - mService.getRecentsAnimationController(); - final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null - && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord); - if (w.mInputChannelToken == null || w.mRemoved - || (!w.canReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { + if (w.mInputChannelToken == null || w.mRemoved || !w.canReceiveTouchInput()) { if (w.mWinAnimator.hasSurface()) { // Make sure the input info can't receive input event. It may be omitted from // occlusion detection depending on the type or if it's a trusted overlay. @@ -566,6 +561,10 @@ final class InputMonitor { final int privateFlags = w.mAttrs.privateFlags; // This only works for legacy transitions. + final RecentsAnimationController recentsAnimationController = + mService.getRecentsAnimationController(); + final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null + && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord); if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { if (recentsAnimationController.updateInputConsumerForApp( mRecentsAnimationInputConsumer.mWindowHandle)) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3b282aada7ae6..1abe24e926fe1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3220,19 +3220,7 @@ class WindowState extends WindowContainer implements WindowManagerP } return !mActivityRecord.getTask().getRootTask().shouldIgnoreInput() - && mActivityRecord.mVisibleRequested - && !isRecentsAnimationConsumingAppInput(); - } - - /** - * Returns {@code true} if the window is animating to home as part of the recents animation and - * it is consuming input from the app. - */ - private boolean isRecentsAnimationConsumingAppInput() { - final RecentsAnimationController recentsAnimationController = - mWmService.getRecentsAnimationController(); - return recentsAnimationController != null - && recentsAnimationController.shouldApplyInputConsumer(mActivityRecord); + && mActivityRecord.mVisibleRequested; } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index b6998d84afa35..724204f79eabe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -730,17 +730,6 @@ public class WindowStateTests extends WindowTestsBase { assertThat(mWm.mResizingWindows).doesNotContain(win); } - @Test - public void testCantReceiveTouchDuringRecentsAnimation() { - final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); - - // Mock active recents animation - RecentsAnimationController recentsController = mock(RecentsAnimationController.class); - when(recentsController.shouldApplyInputConsumer(win0.mActivityRecord)).thenReturn(true); - mWm.setRecentsAnimationController(recentsController); - assertFalse(win0.canReceiveTouchInput()); - } - @Test public void testCantReceiveTouchWhenAppTokenHiddenRequested() { final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); From 03df66775f830c42899b8b599d03fafe6a8bbceb Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Sat, 14 May 2022 00:08:43 +0000 Subject: [PATCH 2/2] Add workaround for new back flow with legacy recents animation - Override the callback to inject back as we do today while in Overview. We can't rely on the client side compat callback because the Launcher window is not focused in this current state. Bug: 223750399 Test: Open overview, swipe back Change-Id: I1978ce3a91cba3e57c0f8bab366691b48a9d5921 --- data/etc/services.core.protolog.json | 12 ++--- .../server/wm/BackNavigationController.java | 25 ++++++++- .../server/wm/RecentsAnimationController.java | 53 +++++++++++++++++++ 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 12d3d642a862d..51ed8567063ea 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1657,6 +1657,12 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/Task.java" }, + "-451552570": { + "message": "Current focused window being animated by recents. Overriding back callback to recents controller callback.", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "-449118559": { "message": "Trying to update display configuration for invalid process, pid=%d", "level": "WARN", @@ -2917,12 +2923,6 @@ "group": "WM_DEBUG_LOCKTASK", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, - "716528224": { - "message": "Focused window found using wmService.getFocusedWindowLocked()", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "726205185": { "message": "Moving to DESTROYED: %s (destroy skipped)", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 247117e707c85..dac72d8202512 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -145,6 +145,27 @@ class BackNavigationController { "Focused window found using getFocusedWindowToken"); } + OnBackInvokedCallbackInfo overrideCallbackInfo = null; + if (window != null) { + // This is needed to bridge the old and new back behavior with recents. While in + // Overview with live tile enabled, the previous app is technically focused but we + // add an input consumer to capture all input that would otherwise go to the apps + // being controlled by the animation. This means that the window resolved is not + // the right window to consume back while in overview, so we need to route it to + // launcher and use the legacy behavior of injecting KEYCODE_BACK since the existing + // compat callback in VRI only works when the window is focused. + final RecentsAnimationController recentsAnimationController = + wmService.getRecentsAnimationController(); + if (recentsAnimationController != null + && recentsAnimationController.shouldApplyInputConsumer( + window.getActivityRecord())) { + window = recentsAnimationController.getTargetAppMainWindow(); + overrideCallbackInfo = recentsAnimationController.getBackInvokedInfo(); + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Current focused window being animated by " + + "recents. Overriding back callback to recents controller callback."); + } + } + if (window == null) { // We don't have any focused window, fallback ont the top currentTask of the focused // display. @@ -159,7 +180,9 @@ class BackNavigationController { if (window != null) { currentActivity = window.mActivityRecord; currentTask = window.getTask(); - callbackInfo = window.getOnBackInvokedCallbackInfo(); + callbackInfo = overrideCallbackInfo != null + ? overrideCallbackInfo + : window.getOnBackInvokedCallbackInfo(); if (callbackInfo == null) { Slog.e(TAG, "No callback registered, returning null."); return null; diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 2bae59a930480..7ba656ba2819e 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -19,10 +19,12 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.hardware.input.InputManager.INJECT_INPUT_EVENT_MODE_ASYNC; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; @@ -39,6 +41,7 @@ import android.graphics.GraphicBuffer; import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; +import android.hardware.input.InputManager; import android.os.Binder; import android.os.IBinder.DeathRecipient; import android.os.RemoteException; @@ -51,12 +54,18 @@ import android.util.SparseBooleanArray; import android.util.proto.ProtoOutputStream; import android.view.IRecentsAnimationController; import android.view.IRecentsAnimationRunner; +import android.view.InputDevice; import android.view.InputWindowHandle; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; import android.view.WindowInsets.Type; +import android.window.BackEvent; +import android.window.IOnBackInvokedCallback; +import android.window.OnBackInvokedCallbackInfo; import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; @@ -194,6 +203,46 @@ public class RecentsAnimationController implements DeathRecipient { } }; + /** + * Back invoked callback for legacy recents transition with the new back dispatch system. + */ + final IOnBackInvokedCallback mBackCallback = new IOnBackInvokedCallback.Stub() { + @Override + public void onBackStarted() { + // Do nothing + } + + @Override + public void onBackProgressed(BackEvent backEvent) { + // Do nothing + } + + @Override + public void onBackCancelled() { + // Do nothing + } + + @Override + public void onBackInvoked() { + sendBackEvent(KeyEvent.ACTION_DOWN); + sendBackEvent(KeyEvent.ACTION_UP); + } + + private void sendBackEvent(int action) { + if (mTargetActivityRecord == null) { + return; + } + long when = SystemClock.uptimeMillis(); + final KeyEvent ev = new KeyEvent(when, when, action, + KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, + KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, + InputDevice.SOURCE_KEYBOARD); + ev.setDisplayId(mTargetActivityRecord.getDisplayId()); + InputManager.getInstance().injectInputEvent(ev, INJECT_INPUT_EVENT_MODE_ASYNC); + } + }; + public interface RecentsAnimationCallbacks { /** Callback when recents animation is finished. */ void onAnimationFinished(@ReorderMode int reorderMode, boolean sendUserLeaveHint); @@ -1104,6 +1153,10 @@ public class RecentsAnimationController implements DeathRecipient { return mTargetActivityRecord.findMainWindow(); } + OnBackInvokedCallbackInfo getBackInvokedInfo() { + return new OnBackInvokedCallbackInfo(mBackCallback, PRIORITY_DEFAULT); + } + DisplayArea getTargetAppDisplayArea() { if (mTargetActivityRecord == null) { return null;