Inject back key events if no focus window found.
If user applied a back gesture but core cannot find a focus window,
inject back key event, so that event can dispatch to client once it
gain focus window. And since the gesture was happened, treat it as
onBackPressed.
And if device is playing recents animation but the focus window is
point to live tile, we can also inject back key because Home/Recents
activity shall not have the same behavior as normal app.
Bug: 237629607
Test: cross test that back gesture can finish recents with legacy/shell
transition on Nexus/3rd launcher.
Test: test on BackTestApp for both 3btn mode and gesture mode.
Test: atest AnrTests BackNavigationLegacyGestureTest
Test: atest OnBackInvokedDispatcherTest KeyboardVisibilityControlTest
BackInvokedOnWidgetTest BackNavigationTests BackNavigationControllerTests
Change-Id: If754e66ea7c1c4ec313c45d17d7f6c10bfc2c465
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8ec32a6..9082165 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10858,8 +10858,8 @@
private void registerCompatOnBackInvokedCallback() {
mCompatOnBackInvokedCallback = () -> {
- sendBackKeyEvent(KeyEvent.ACTION_DOWN);
- sendBackKeyEvent(KeyEvent.ACTION_UP);
+ sendBackKeyEvent(KeyEvent.ACTION_DOWN);
+ sendBackKeyEvent(KeyEvent.ACTION_UP);
};
mOnBackInvokedDispatcher.registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback);
diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
index 8ad1093..49acde9 100644
--- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
@@ -122,7 +122,7 @@
}
for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) {
int priority = callbackPair.second;
- if (priority >= 0) {
+ if (priority >= PRIORITY_DEFAULT) {
mActualDispatcher.registerOnBackInvokedCallback(priority, callbackPair.first);
} else {
mActualDispatcher.registerSystemOnBackInvokedCallback(callbackPair.first);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 0221122..05fafc5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -30,15 +30,20 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.hardware.HardwareBuffer;
+import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.util.Log;
import android.view.IWindowFocusObserver;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -292,6 +297,9 @@
} else if (keyAction == MotionEvent.ACTION_UP || keyAction == MotionEvent.ACTION_CANCEL) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW,
"Finishing gesture with event action: %d", keyAction);
+ if (keyAction == MotionEvent.ACTION_CANCEL) {
+ mTriggerBack = false;
+ }
onGestureFinished(true);
}
}
@@ -321,7 +329,6 @@
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Received backNavigationInfo:%s", backNavigationInfo);
if (backNavigationInfo == null) {
Log.e(TAG, "Received BackNavigationInfo is null.");
- finishAnimation();
return;
}
int backType = backNavigationInfo.getType();
@@ -397,6 +404,25 @@
dispatchOnBackProgressed(targetCallback, backEvent);
}
+ private void injectBackKey() {
+ sendBackEvent(KeyEvent.ACTION_DOWN);
+ sendBackEvent(KeyEvent.ACTION_UP);
+ }
+
+ private void sendBackEvent(int action) {
+ final 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(mContext.getDisplay().getDisplayId());
+ if (!InputManager.getInstance()
+ .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) {
+ Log.e(TAG, "Inject input event fail");
+ }
+ }
+
private void onGestureFinished(boolean fromTouch) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
if (fromTouch) {
@@ -405,7 +431,17 @@
mBackGestureStarted = false;
}
- if (mTransitionInProgress || mBackNavigationInfo == null) {
+ if (mTransitionInProgress) {
+ return;
+ }
+
+ if (mBackNavigationInfo == null) {
+ // No focus window found or core are running recents animation, inject back key as
+ // legacy behavior.
+ if (mTriggerBack) {
+ injectBackKey();
+ }
+ finishAnimation();
return;
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index f967cf9..d9ab971 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -131,7 +131,6 @@
"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
@@ -140,15 +139,18 @@
// 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.
+ // This symptom also happen while shell transition enabled, we can check that by
+ // isTransientLaunch to know whether the focus window is point to live tile.
final RecentsAnimationController recentsAnimationController =
wmService.getRecentsAnimationController();
- if (recentsAnimationController != null
- && recentsAnimationController.shouldApplyInputConsumer(
- window.getActivityRecord())) {
- window = recentsAnimationController.getTargetAppMainWindow();
- overrideCallbackInfo = recentsAnimationController.getBackInvokedInfo();
+ final ActivityRecord ar = window.mActivityRecord;
+ if ((ar != null && ar.isActivityTypeHomeOrRecents()
+ && ar.mTransitionController.isTransientLaunch(ar))
+ || (recentsAnimationController != null
+ && recentsAnimationController.shouldApplyInputConsumer(ar))) {
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Current focused window being animated by "
+ "recents. Overriding back callback to recents controller callback.");
+ return null;
}
}
@@ -166,9 +168,7 @@
if (window != null) {
currentActivity = window.mActivityRecord;
currentTask = window.getTask();
- callbackInfo = overrideCallbackInfo != null
- ? overrideCallbackInfo
- : window.getOnBackInvokedCallbackInfo();
+ callbackInfo = window.getOnBackInvokedCallbackInfo();
if (callbackInfo == null) {
Slog.e(TAG, "No callback registered, returning null.");
return null;
@@ -213,8 +213,10 @@
Task finalTask = currentTask;
prevActivity = currentTask.getActivity(
(r) -> !r.finishing && r.getTask() == finalTask && !r.isTopRunningActivity());
- if (window.getParent().getChildCount() > 1 && window.getParent().getChildAt(0)
- != window) {
+ // TODO Dialog window does not need to attach on activity, check
+ // window.mAttrs.type != TYPE_BASE_APPLICATION
+ if ((window.getParent().getChildCount() > 1
+ && window.getParent().getChildAt(0) != window)) {
// Are we the top window of our parent? If not, we are a window on top of the
// activity, we won't close the activity.
backType = BackNavigationInfo.TYPE_DIALOG_CLOSE;
@@ -379,7 +381,8 @@
private void onBackNavigationDone(
Bundle result, WindowState focusedWindow, WindowContainer<?> windowContainer,
- int backType, Task task, ActivityRecord prevActivity, boolean prepareAnimation) {
+ int backType, @Nullable Task task, @Nullable ActivityRecord prevActivity,
+ boolean prepareAnimation) {
SurfaceControl surfaceControl = windowContainer.getSurfaceControl();
boolean triggerBack = result != null && result.getBoolean(
BackNavigationInfo.KEY_TRIGGER_BACK);
@@ -404,7 +407,7 @@
"Setting Activity.mLauncherTaskBehind to false. Activity=%s",
prevActivity);
}
- } else {
+ } else if (task != null) {
task.mBackGestureStarted = false;
}
resetSurfaces(windowContainer);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 53f1fe6..1cff693 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -19,12 +19,10 @@
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;
@@ -41,7 +39,6 @@
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;
@@ -54,18 +51,12 @@
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;
@@ -195,46 +186,6 @@
}
};
- /**
- * 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);
@@ -1112,10 +1063,6 @@
return mTargetActivityRecord.findMainWindow();
}
- OnBackInvokedCallbackInfo getBackInvokedInfo() {
- return new OnBackInvokedCallbackInfo(mBackCallback, PRIORITY_DEFAULT);
- }
-
DisplayArea getTargetAppDisplayArea() {
if (mTargetActivityRecord == null) {
return null;