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 {
@Override
public 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 user
final 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 WM
if (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.
*/
@VisibleForTesting
void 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 {
@Override
void 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();
}