From 4eb5845678b187fe13f3073e2197505b9defb963 Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Wed, 19 Apr 2023 16:39:57 -0700 Subject: [PATCH] Fix some incompatibilities of 3p launcher and recentstransition Since Launcher talks to both recentstransition and WMCore via binder, it can result in races. Because 3p launchers are treated half like apps (to recents), launcher would use normal activity calls to control the 3p launcher lifecycle. It would sometimes launch the app AND set focus at similar times. Because setfocus went through recentstransition, it could race and cause the focus change to happen after the activity-launch which would confuse recents. To fix this, add a special focus API for recents which says "focus top task" since all recents really needs is to tell WM to move focus to itself (transient launch defers focus change until the animation requests it). By just focusing "top" task, it should never result in a re-ordering but still work for recents use-case. Additionally, recentstransition was seeing 3p launcher as a regular (non-home) task. This meant that it would treat a non-committed gesture as a task-switch and "switch away" from the 3p launcher. So this CL detects when the gesture starts on 3p launcher and then uses that information to categorize uncommit properly. Bug: 277964651 Test: Use a 3p launcher (like Nova) and use gestures. Change-Id: I6511db60c5a92d2fa4418b95369e069142b3a00c --- .../android/app/IActivityTaskManager.aidl | 3 ++ .../recents/RecentsTransitionHandler.java | 30 +++++++++++++++---- .../server/wm/ActivityTaskManagerService.java | 18 +++++++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index d62e15a94f89b..3249b41da934c 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -174,6 +174,9 @@ interface IActivityTaskManager { ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo(); Rect getTaskBounds(int taskId); + /** Focuses the top task on a display if it isn't already focused. Used for Recents. */ + void focusTopTask(int displayId); + void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES)") void updateLockTaskPackages(int userId, in String[] packages); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index 4f362d4ed0d8d..d22ed2b6f94e5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -36,6 +36,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Slog; +import android.view.Display; import android.view.IRecentsAnimationController; import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationTarget; @@ -219,6 +220,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { private int mRecentsTaskId = -1; private TransitionInfo mInfo = null; private boolean mOpeningSeparateHome = false; + private boolean mPausingSeparateHome = false; private ArrayMap mLeashMap = null; private PictureInPictureSurfaceTransaction mPipTransaction = null; private IBinder mTransition = null; @@ -407,6 +409,10 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // raise closing (pausing) task to "above" layer so it isn't covered t.setLayer(target.leash, info.getChanges().size() * 3 - i); mPausingTasks.add(new TaskState(change, target.leash)); + if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { + // This can only happen if we have a separate recents/home (3p launcher) + mPausingSeparateHome = true; + } if (taskInfo.pictureInPictureParams != null && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) { mPipTask = taskInfo.token; @@ -657,6 +663,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mFinishCB != null, enabled); return; } + final int displayId = mInfo.getRootCount() > 0 ? mInfo.getRoot(0).getDisplayId() + : Display.DEFAULT_DISPLAY; // transient launches don't receive focus automatically. Since we are taking over // the gesture now, take focus explicitly. // This also moves recents back to top if the user gestured before a switch @@ -665,7 +673,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.setInputConsumerEnabled: set focus to recents", mInstanceId); - ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId); + ActivityTaskManager.getService().focusTopTask(displayId); } catch (RemoteException e) { Slog.e(TAG, "Failed to set focused task", e); } @@ -700,8 +708,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { return; } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - "[%d] RecentsController.finishInner: toHome=%b userLeave=%b willFinishToHome=%b", - mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome); + "[%d] RecentsController.finishInner: toHome=%b userLeave=%b " + + "willFinishToHome=%b state=%d", + mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState); final Transitions.TransitionFinishCallback finishCB = mFinishCB; mFinishCB = null; @@ -712,8 +721,19 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { if (toHome) wct.reorder(mRecentsTask, true /* toTop */); else wct.restoreTransientOrder(mRecentsTask); } - if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " returning to app"); + if (!toHome + // If a recents gesture starts on the 3p launcher, then the 3p launcher is the + // live tile (pausing app). If the gesture is "cancelled" we need to return to + // 3p launcher instead of "task-switching" away from it. + && (!mWillFinishToHome || mPausingSeparateHome) + && mPausingTasks != null && mState == STATE_NORMAL) { + if (mPausingSeparateHome) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " returning to 3p home"); + } else { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " returning to app"); + } // The gesture is returning to the pausing-task(s) rather than continuing with // recents, so end the transition by moving the app back to the top (and also // re-showing it's task). diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index f93afe81f8046..ea0731ae346a7 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1996,6 +1996,24 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @Override + public void focusTopTask(int displayId) { + enforceTaskPermission("focusTopTask()"); + final long callingId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId); + if (dc == null) return; + final Task task = dc.getTask((t) -> t.isLeafTask() && t.isFocusable(), + true /* traverseTopToBottom */); + if (task == null) return; + setFocusedTask(task.mTaskId, null /* touchedActivity */); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + void setFocusedTask(int taskId, ActivityRecord touchedActivity) { ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d touchedActivity=%s", taskId, touchedActivity);