ActivityManager.AppTask.moveToFront()执行后,导致其他AppTask退到了后台,点击返回直接回到了桌面(HomeScreen),没有回到上一个AppTask。
下面分析一下源码看看为什么其他AppTask退到了后台,如何解决该问题。
@SystemService(Context.ACTIVITY_SERVICE)
public class ActivityManager {/*** The AppTask allows you to manage your own application's tasks.* See {@link android.app.ActivityManager#getAppTasks()}*/public static class AppTask {private IAppTask mAppTaskImpl;/** @hide */public AppTask(IAppTask task) {mAppTaskImpl = task;}/*** Bring this task to the foreground. If it contains activities, they will be* brought to the foreground with it and their instances re-created if needed.* If it doesn't contain activities, the root activity of the task will be* re-launched.*/public void moveToFront() {try {ActivityThread thread = ActivityThread.currentActivityThread();IApplicationThread appThread = thread.getApplicationThread();String packageName = ActivityThread.currentPackageName();mAppTaskImpl.moveToFront(appThread, packageName);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}
/*** An implementation of IAppTask, that allows an app to manage its own tasks via* {@link android.app.ActivityManager.AppTask}. We keep track of the callingUid to ensure that* only the process that calls getAppTasks() can call the AppTask methods.*/
class AppTaskImpl extends IAppTask.Stub {@Overridepublic void moveToFront(IApplicationThread appThread, String callingPackage) {checkCaller();// Will bring task to front if it already has a root activity.final int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();mService.assertPackageMatchesCallingUid(callingPackage);final long origId = Binder.clearCallingIdentity();try {synchronized (mService.mGlobalLock) {WindowProcessController callerApp = null;if (appThread != null) {callerApp = mService.getProcessController(appThread);}final ActivityStarter starter = mService.getActivityStartController().obtainStarter(null /* intent */, "moveToFront");if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid,callingPackage, -1, -1, callerApp, null, false, null)) {if (!mService.isBackgroundActivityStartsEnabled()) {return;}}}mService.mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId,null /* options */);} finally {Binder.restoreCallingIdentity(origId);}}
// TODO: This class has become a dumping ground. Let's
// - Move things relating to the hierarchy to RootWindowContainer
// - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler
// - Move interface things to ActivityTaskManagerService.
// - All other little things to other files.
public class ActivityTaskSupervisor implements RecentTasks.Callbacks {/*** Start the given task from the recent tasks. Do not hold WM global lock when calling this* method to avoid potential deadlock or permission deny by UriGrantsManager when resolving* activity (see {@link ActivityStarter.Request#resolveActivity} and* {@link com.android.server.am.ContentProviderHelper#checkContentProviderUriPermission}).** @return The result code of starter.*/int startActivityFromRecents(int callingPid, int callingUid, int taskId,SafeActivityOptions options) {final Task task;final int taskCallingUid;final String callingPackage;final String callingFeatureId;final Intent intent;final int userId;final ActivityOptions activityOptions = options != null? options.getOptions(this): null;boolean moveHomeTaskForward = true;synchronized (mService.mGlobalLock) {int activityType = ACTIVITY_TYPE_UNDEFINED;if (activityOptions != null) {activityType = activityOptions.getLaunchActivityType();final int windowingMode = activityOptions.getLaunchWindowingMode();if (activityOptions.freezeRecentTasksReordering()&& mService.checkPermission(MANAGE_ACTIVITY_TASKS, callingPid, callingUid)== PERMISSION_GRANTED) {mRecentTasks.setFreezeTaskListReordering();}if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY|| activityOptions.getLaunchRootTask() != null) {// Don't move home activity forward if we are launching into primary split or// there is a launch root set.moveHomeTaskForward = false;}}if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {throw new IllegalArgumentException("startActivityFromRecents: Task "+ taskId + " can't be launch in the home/recents root task.");}boolean shouldStartActivity = false;mService.deferWindowLayout();try {task = mRootWindowContainer.anyTaskForId(taskId,MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);if (task == null) {mWindowManager.executeAppTransition();throw new IllegalArgumentException("startActivityFromRecents: Task " + taskId + " not found.");}if (moveHomeTaskForward) {// We always want to return to the home activity instead of the recents// activity from whatever is started from the recents activity, so move// the home root task forward.// TODO (b/115289124): Multi-display supports for recents.mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront("startActivityFromRecents");}// If the user must confirm credentials (e.g. when first launching a work// app and the Work Challenge is present) let startActivityInPackage handle// the intercepting.if (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId)&& task.getRootActivity() != null) {final ActivityRecord targetActivity = task.getTopNonFinishingActivity();mRootWindowContainer.startPowerModeLaunchIfNeeded(true /* forceSend */, targetActivity);final LaunchingState launchingState =mActivityMetricsLogger.notifyActivityLaunching(task.intent);try {mService.moveTaskToFrontLocked(null /* appThread */,null /* callingPackage */, task.mTaskId, 0, options);// Apply options to prevent pendingOptions be taken when scheduling// activity lifecycle transaction to make sure the override pending app// transition will be applied immediately.targetActivity.applyOptionsAnimation();} finally {mActivityMetricsLogger.notifyActivityLaunched(launchingState,START_TASK_TO_FRONT, false /* newActivityCreated */,targetActivity, activityOptions);}mService.getActivityStartController().postStartActivityProcessingForLastStarter(task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT,task.getRootTask());// As it doesn't go to ActivityStarter.executeRequest() path, we need to resume// app switching here also.mService.resumeAppSwitches();return ActivityManager.START_TASK_TO_FRONT;}// The task is empty or needs to show the confirmation for credential.shouldStartActivity = true;} finally {if (!shouldStartActivity) {mService.continueWindowLayout();}}taskCallingUid = task.mCallingUid;callingPackage = task.mCallingPackage;callingFeatureId = task.mCallingFeatureId;intent = task.intent;intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);userId = task.mUserId;}// ActivityStarter will acquire the lock where the places need, so execute the request// outside of the lock.try {return mService.getActivityStartController().startActivityInPackage(taskCallingUid,callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,null, 0, 0, options, userId, task, "startActivityFromRecents",false /* validateIncomingUser */, null /* originatingPendingIntent */,false /* allowBackgroundActivityStart */);} finally {synchronized (mService.mGlobalLock) {mService.continueWindowLayout();}}}
/*** System service for managing activities and their containers (task, displays,... ).** {@hide}*/
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {void moveTaskToFrontLocked(@Nullable IApplicationThread appThread,@Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options) {final int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();assertPackageMatchesCallingUid(callingPackage);final long origId = Binder.clearCallingIdentity();WindowProcessController callerApp = null;if (appThread != null) {callerApp = getProcessController(appThread);}final ActivityStarter starter = getActivityStartController().obtainStarter(null /* intent */, "moveTaskToFront");if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1,-1, callerApp, null, false, null)) {if (!isBackgroundActivityStartsEnabled()) {return;}}try {final Task task = mRootWindowContainer.anyTaskForId(taskId);if (task == null) {ProtoLog.d(WM_DEBUG_TASKS, "Could not find task for id: %d", taskId);SafeActivityOptions.abort(options);return;}if (getLockTaskController().isLockTaskModeViolation(task)) {Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");SafeActivityOptions.abort(options);return;}ActivityOptions realOptions = options != null? options.getOptions(mTaskSupervisor): null;mTaskSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",false /* forceNonResizable */);final ActivityRecord topActivity = task.getTopNonFinishingActivity();if (topActivity != null) {// We are reshowing a task, use a starting window to hide the initial draw delay// so the transition can start earlier.topActivity.showStartingWindow(true /* taskSwitch */);}} finally {Binder.restoreCallingIdentity(origId);}}
// TODO: This class has become a dumping ground. Let's
// - Move things relating to the hierarchy to RootWindowContainer
// - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler
// - Move interface things to ActivityTaskManagerService.
// - All other little things to other files.
public class ActivityTaskSupervisor implements RecentTasks.Callbacks {/** This doesn't just find a task, it also moves the task to front. */void findTaskToMoveToFront(Task task, int flags, ActivityOptions options, String reason,boolean forceNonResizeable) {Task currentRootTask = task.getRootTask();if (currentRootTask == null) {Slog.e(TAG, "findTaskToMoveToFront: can't move task="+ task + " to front. Root task is null");return;}try {if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {mUserLeaving = true;}task.mTransitionController.requestTransitionIfNeeded(TRANSIT_TO_FRONT,0 /* flags */, task, task /* readyGroupRef */,options != null ? options.getRemoteTransition() : null);reason = reason + " findTaskToMoveToFront";boolean reparented = false;if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {final Rect bounds = options.getLaunchBounds();task.setBounds(bounds);Task launchRootTask =mRootWindowContainer.getLaunchRootTask(null, options, task, ON_TOP);if (launchRootTask != currentRootTask) {moveHomeRootTaskToFrontIfNeeded(flags, launchRootTask.getDisplayArea(), reason);task.reparent(launchRootTask, ON_TOP, REPARENT_KEEP_ROOT_TASK_AT_FRONT,!ANIMATE, DEFER_RESUME, reason);currentRootTask = launchRootTask;reparented = true;// task.reparent() should already placed the task on top,// still need moveTaskToFrontLocked() below for any transition settings.}if (launchRootTask.shouldResizeRootTaskWithLaunchBounds()) {launchRootTask.resize(bounds, !PRESERVE_WINDOWS, !DEFER_RESUME);} else {// WM resizeTask must be done after the task is moved to the correct stack,// because Task's setBounds() also updates dim layer's bounds, but that has// dependency on the root task.task.resize(false /* relayout */, false /* forced */);}}if (!reparented) {moveHomeRootTaskToFrontIfNeeded(flags, currentRootTask.getDisplayArea(), reason);}final ActivityRecord r = task.getTopNonFinishingActivity();currentRootTask.moveTaskToFront(task, false /* noAnimation */, options,r == null ? null : r.appTimeTracker, reason);if (DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,"findTaskToMoveToFront: moved to front of root task=" + currentRootTask);handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,mRootWindowContainer.getDefaultTaskDisplayArea(), currentRootTask,forceNonResizeable);} finally {mUserLeaving = false;}}
/*** {@link Task} is a TaskFragment that can contain a group of activities to perform a certain job.* Activities of the same task affinities usually group in the same {@link Task}. A {@link Task}* can also be an entity that showing in the Recents Screen for a job that user interacted with.* A {@link Task} can also contain other {@link Task}s.*/
class Task extends TaskFragment {final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,AppTimeTracker timeTracker, String reason) {moveTaskToFront(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);}final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,AppTimeTracker timeTracker, boolean deferResume, String reason) {if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);final Task topRootTask = getDisplayArea().getTopRootTask();final ActivityRecord topActivity = topRootTask != null? topRootTask.getTopNonFinishingActivity() : null;if (tr != this && !tr.isDescendantOf(this)) {// nothing to do!if (noAnimation) {ActivityOptions.abort(options);} else {updateTransitLocked(TRANSIT_TO_FRONT, options);}return;}if (timeTracker != null) {// The caller wants a time tracker associated with this task.final PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setAppTimeTracker,PooledLambda.__(ActivityRecord.class), timeTracker);tr.forAllActivities(c);c.recycle();}try {// Defer updating the IME target since the new IME target will try to get computed// before updating all closing and opening apps, which can cause the ime target to// get calculated incorrectly.mDisplayContent.deferUpdateImeTarget();// Don't refocus if invisible to current userfinal ActivityRecord top = tr.getTopNonFinishingActivity();if (top == null || !top.showToCurrentUser()) {positionChildAtTop(tr);if (top != null) {mTaskSupervisor.mRecentTasks.add(top.getTask());}ActivityOptions.abort(options);return;}// Set focus to the top running activity of this task and move all its parents to top.top.moveFocusableActivityToTop(reason);if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);if (noAnimation) {mDisplayContent.prepareAppTransition(TRANSIT_NONE);mTaskSupervisor.mNoAnimActivities.add(top);ActivityOptions.abort(options);} else {updateTransitLocked(TRANSIT_TO_FRONT, options);}// If a new task is moved to the front, then mark the existing top activity as// supporting// picture-in-picture while paused only if the task would not be considered an oerlay// on top// of the current activity (eg. not fullscreen, or the assistant)if (canEnterPipOnTaskSwitch(topActivity, tr, null /* toFrontActivity */,options)) {topActivity.supportsEnterPipOnTaskSwitch = true;}if (!deferResume) {mRootWindowContainer.resumeFocusedTasksTopActivities();}} finally {mDisplayContent.continueUpdateImeTarget();}}
/*** An entry in the history task, representing an activity.*/
final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {/*** Move activity with its root task to front and make the root task focused.* @param reason the reason to move to top* @return {@code true} if the root task is focusable and has been moved to top or the activity* is not yet resumed while the root task is already on top, {@code false} otherwise.*/boolean moveFocusableActivityToTop(String reason) {if (!isFocusable()) {ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: unfocusable "+ "activity=%s", this);return false;}final Task rootTask = getRootTask();if (rootTask == null) {Slog.w(TAG, "moveFocusableActivityToTop: invalid root task: activity="+ this + " task=" + task);return false;}if (mRootWindowContainer.getTopResumedActivity() == this&& getDisplayContent().mFocusedApp == this) {ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: already on top, "+ "activity=%s", this);return !isState(RESUMED);}ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: activity=%s", this);rootTask.moveToFront(reason, task);// Report top activity change to tracking services and WMif (mRootWindowContainer.getTopResumedActivity() == this) {mAtmService.setResumedActivityUncheckLocked(this, reason);}return true;}
/*** {@link Task} is a TaskFragment that can contain a group of activities to perform a certain job.* Activities of the same task affinities usually group in the same {@link Task}. A {@link Task}* can also be an entity that showing in the Recents Screen for a job that user interacted with.* A {@link Task} can also contain other {@link Task}s.*/
class Task extends TaskFragment {void moveToFront(String reason, Task task) {if (inSplitScreenSecondaryWindowingMode()) {// If the root task is in split-screen secondary mode, we need to make sure we move the// primary split-screen root task forward in the case it is currently behind a// fullscreen root task so both halves of the split-screen appear on-top and the// fullscreen root task isn't cutting between them.// TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.final TaskDisplayArea taskDisplayArea = getDisplayArea();final Task topFullScreenRootTask =taskDisplayArea.getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);if (topFullScreenRootTask != null) {final Task primarySplitScreenRootTask =taskDisplayArea.getRootSplitScreenPrimaryTask();if (primarySplitScreenRootTask != null&& topFullScreenRootTask.compareTo(primarySplitScreenRootTask) > 0) {primarySplitScreenRootTask.moveToFrontInner(reason + " splitScreenToTop",null /* task */);}}} else if (mMoveAdjacentTogether && getAdjacentTaskFragment() != null) {final Task adjacentTask = getAdjacentTaskFragment().asTask();if (adjacentTask != null) {adjacentTask.moveToFrontInner(reason + " adjacentTaskToTop", null /* task */);}}moveToFrontInner(reason, task);}/*** @param reason The reason for moving the root task to the front.* @param task If non-null, the task will be moved to the top of the root task.*/@VisibleForTestingvoid moveToFrontInner(String reason, Task task) {if (!isAttached()) {return;}final TaskDisplayArea taskDisplayArea = getDisplayArea();if (!isActivityTypeHome() && returnsToHomeRootTask()) {// Make sure the root home task is behind this root task since that is where we// should return to when this root task is no longer visible.taskDisplayArea.moveHomeRootTaskToFront(reason + " returnToHome");}final Task lastFocusedTask = isRootTask() ? taskDisplayArea.getFocusedRootTask() : null;if (task == null) {task = this;}task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);taskDisplayArea.updateLastFocusedRootTask(lastFocusedTask, reason);}
/*** {@link Task} is a TaskFragment that can contain a group of activities to perform a certain job.* Activities of the same task affinities usually group in the same {@link Task}. A {@link Task}* can also be an entity that showing in the Recents Screen for a job that user interacted with.* A {@link Task} can also contain other {@link Task}s.*/
class Task extends TaskFragment {@Overridevoid positionChildAt(int position, WindowContainer child, boolean includingParents) {final boolean toTop = position >= (mChildren.size() - 1);position = getAdjustedChildPosition(child, position);super.positionChildAt(position, child, includingParents);// Log positioning.if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "positionChildAt: child=" + child+ " position=" + position + " parent=" + this);final Task task = child.asTask();if (task != null) {task.updateTaskMovement(toTop, position);}}void updateTaskMovement(boolean toTop, int position) {EventLogTags.writeWmTaskMoved(mTaskId, toTop ? 1 : 0, position);final TaskDisplayArea taskDisplayArea = getDisplayArea();if (taskDisplayArea != null && isLeafTask()) {taskDisplayArea.onLeafTaskMoved(this, toTop);}if (isPersistable) {mLastTimeMoved = System.currentTimeMillis();}}
/*** {@link DisplayArea} that represents a section of a screen that contains app window containers.** The children can be either {@link Task} or {@link TaskDisplayArea}.*/
final class TaskDisplayArea extends DisplayArea<WindowContainer> {void onLeafTaskMoved(Task t, boolean toTop) {if (!toTop) {if (t.mTaskId == mLastLeafTaskToFrontId) {mLastLeafTaskToFrontId = INVALID_TASK_ID;}return;}if (t.mTaskId == mLastLeafTaskToFrontId || t.topRunningActivityLocked() == null) {return;}mLastLeafTaskToFrontId = t.mTaskId;EventLogTags.writeWmTaskToFront(t.mUserId, t.mTaskId);// Notifying only when a leaf task moved to front. Or the listeners would be notified// couple times from the leaf task all the way up to the root task.mAtmService.getTaskChangeNotificationController().notifyTaskMovedToFront(t.getTaskInfo());}
class TaskChangeNotificationController {void notifyTaskMovedToFront(TaskInfo ti) {final Message msg = mHandler.obtainMessage(NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG, ti);forAllLocalListeners(mNotifyTaskMovedToFront, msg);msg.sendToTarget();}