Loading core/res/res/values/attrs_manifest.xml +5 −1 Original line number Diff line number Diff line Loading @@ -1315,7 +1315,11 @@ it will yield the baseIntent to any activity that it launches in the same task. This continues until an activity is encountered which has this attribute set to false. False is the default. This attribute set to true also permits activity's use of the TaskDescription to change labels, colors and icons in the recent task list. --> TaskDescription to change labels, colors and icons in the recent task list. <p>NOTE: Setting this flag to <code>true</code> will not change the affinity of the task, which is used for intent resolution during activity launch. The task's root activity will always define its affinity. --> <attr name="relinquishTaskIdentity" format="boolean" /> <!-- Indicate that it is okay for this activity be resumed while the previous Loading services/core/java/com/android/server/wm/ActivityRecord.java +25 −6 Original line number Diff line number Diff line Loading @@ -319,8 +319,6 @@ final class ActivityRecord extends ConfigurationContainer { private ActivityState mState; // current state we are in Bundle icicle; // last saved activity state PersistableBundle persistentState; // last persistently saved activity state // TODO: See if this is still needed. boolean frontOfTask; // is this the root activity of its task? boolean launchFailed; // set if a launched failed, to abort on 2nd try boolean haveState; // have we gotten the last activity state? boolean stopped; // is activity pause finished? Loading Loading @@ -439,7 +437,7 @@ final class ActivityRecord extends ConfigurationContainer { pw.print(" userId="); pw.println(mUserId); pw.print(prefix); pw.print("app="); pw.println(app); pw.print(prefix); pw.println(intent.toInsecureStringWithClip()); pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask); pw.print(prefix); pw.print("rootOfTask="); pw.print(isRootOfTask()); pw.print(" task="); pw.println(task); pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity); pw.print(prefix); pw.print("mActivityComponent="); Loading Loading @@ -957,7 +955,6 @@ final class ActivityRecord extends ConfigurationContainer { resultWho = _resultWho; requestCode = _reqCode; setState(INITIALIZING, "ActivityRecord ctor"); frontOfTask = false; launchFailed = false; stopped = false; delayedResume = false; Loading Loading @@ -2552,7 +2549,8 @@ final class ActivityRecord extends ConfigurationContainer { } final TaskRecord task = r.task; final int activityNdx = task.mActivities.indexOf(r); if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) { if (activityNdx < 0 || (onlyRoot && activityNdx > task.findRootIndex(true /* effectiveRoot */))) { return INVALID_TASK_ID; } return task.taskId; Loading Loading @@ -3803,6 +3801,27 @@ final class ActivityRecord extends ConfigurationContainer { return display != null && this == display.getResumedActivity(); } /** * Check if this is the root of the task - first activity that is not finishing, starting from * the bottom of the task. If all activities are finishing - then this method will return * {@code true} if the activity is at the bottom. * * NOTE: This is different from 'effective root' - an activity that defines the task identity. */ boolean isRootOfTask() { if (task == null) { return false; } final ActivityRecord rootActivity = task.getRootActivity(); if (rootActivity != null) { return this == rootActivity; } // No non-finishing activity found. In this case the bottom-most activity is considered to // be the root. return task.getChildAt(0) == this; } void registerRemoteAnimations(RemoteAnimationDefinition definition) { if (mAppWindowToken == null) { Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app" Loading Loading @@ -3846,7 +3865,7 @@ final class ActivityRecord extends ConfigurationContainer { writeIdentifierToProto(proto, IDENTIFIER); proto.write(STATE, mState.toString()); proto.write(VISIBLE, visible); proto.write(FRONT_OF_TASK, frontOfTask); proto.write(FRONT_OF_TASK, isRootOfTask()); if (hasProcess()) { proto.write(PROC_ID, app.getPid()); } Loading services/core/java/com/android/server/wm/ActivityStack.java +37 −31 Original line number Diff line number Diff line Loading @@ -1895,7 +1895,7 @@ class ActivityStack extends ConfigurationContainer { // last of activity of the last task the stack will be empty and must // be cleared immediately. boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE || (r.frontOfTask && mTaskHistory.size() <= 1); || (r.isRootOfTask() && mTaskHistory.size() <= 1); if (scheduleIdle || forceIdle) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle=" + forceIdle + "immediate=" + !idleDelayed); Loading Loading @@ -3209,8 +3209,6 @@ class ActivityStack extends ConfigurationContainer { r.createAppWindowToken(); } task.setFrontOfTask(); // The transition animation and starting window are not needed if {@code allowMoveToFront} // is false, because the activity won't be visible. if ((!isHomeOrRecentsStack() || numActivities() > 0) && allowMoveToFront) { Loading Loading @@ -3322,20 +3320,17 @@ class ActivityStack extends ConfigurationContainer { } /** * Perform a reset of the given task, if needed as part of launching it. * Returns the new HistoryRecord at the top of the task. */ /** * Helper method for #resetTaskIfNeededLocked. * We are inside of the task being reset... we'll either finish this activity, push it out * for another task, or leave it as-is. * Helper method for {@link #resetTaskIfNeededLocked(ActivityRecord, ActivityRecord)}. * Performs a reset of the given task, if needed for new activity start. * @param task The task containing the Activity (taskTop) that might be reset. * @param forceReset * @param forceReset Flag indicating if clear task was requested * @return An ActivityOptions that needs to be processed. */ private ActivityOptions resetTargetTaskIfNeededLocked(TaskRecord task, boolean forceReset) { ActivityOptions topOptions = null; // Tracker of the end of currently handled reply chain (sublist) of activities. What happens // to activities in the same chain will depend on what the end activity of the chain needs. int replyChainEnd = -1; boolean canMoveOptions = true; Loading @@ -3343,11 +3338,14 @@ class ActivityStack extends ConfigurationContainer { // the root, we may no longer have the task!). final ArrayList<ActivityRecord> activities = task.mActivities; final int numActivities = activities.size(); final int rootActivityNdx = task.findEffectiveRootIndex(); for (int i = numActivities - 1; i > rootActivityNdx; --i ) { int lastActivityNdx = task.findRootIndex(true /* effectiveRoot */); if (lastActivityNdx == -1) { lastActivityNdx = 0; } for (int i = numActivities - 1; i > lastActivityNdx; --i) { ActivityRecord target = activities.get(i); if (target.frontOfTask) break; // TODO: Why is this needed? Looks like we're breaking the loop before we reach the root if (target.isRootOfTask()) break; final int flags = target.info.flags; final boolean finishOnTaskLaunch = Loading Loading @@ -3380,12 +3378,13 @@ class ActivityStack extends ConfigurationContainer { // bottom of the activity stack. This also keeps it // correctly ordered with any activities we previously // moved. // TODO: We should probably look for other stacks also, since corresponding task // with the same affinity is unlikely to be in the same stack. final TaskRecord targetTask; final ActivityRecord bottom = !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ? mTaskHistory.get(0).mActivities.get(0) : null; if (bottom != null && target.taskAffinity != null && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) { if (bottom != null && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) { // If the activity currently at the bottom has the // same task affinity as the one we are moving, // then merge it into the same task. Loading @@ -3395,7 +3394,8 @@ class ActivityStack extends ConfigurationContainer { } else { targetTask = createTaskRecord( mStackSupervisor.getNextTaskIdForUserLocked(target.mUserId), target.info, null, null, null, false); target.info, null /* intent */, null /* voiceSession */, null /* voiceInteractor */, false /* toTop */); targetTask.affinityIntent = target.intent; if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target + " out to new task " + targetTask); Loading Loading @@ -3476,28 +3476,34 @@ class ActivityStack extends ConfigurationContainer { } /** * Helper method for #resetTaskIfNeededLocked. Processes all of the activities in a given * TaskRecord looking for an affinity with the task of resetTaskIfNeededLocked.taskTop. * Helper method for {@link #resetTaskIfNeededLocked(ActivityRecord, ActivityRecord)}. * Processes all of the activities in a given TaskRecord looking for an affinity with the task * of resetTaskIfNeededLocked.taskTop. * @param affinityTask The task we are looking for an affinity to. * @param task Task that resetTaskIfNeededLocked.taskTop belongs to. * @param topTaskIsHigher True if #task has already been processed by resetTaskIfNeededLocked. * @param forceReset Flag passed in to resetTaskIfNeededLocked. * @param forceReset Flag indicating if clear task was requested */ // TODO: Consider merging with #resetTargetTaskIfNeededLocked() above private int resetAffinityTaskIfNeededLocked(TaskRecord affinityTask, TaskRecord task, boolean topTaskIsHigher, boolean forceReset, int taskInsertionPoint) { // Tracker of the end of currently handled reply chain (sublist) of activities. What happens // to activities in the same chain will depend on what the end activity of the chain needs. int replyChainEnd = -1; final int taskId = task.taskId; final String taskAffinity = task.affinity; final ArrayList<ActivityRecord> activities = affinityTask.mActivities; final int numActivities = activities.size(); final int rootActivityNdx = affinityTask.findEffectiveRootIndex(); // Do not operate on or below the effective root Activity. for (int i = numActivities - 1; i > rootActivityNdx; --i) { int lastActivityNdx = affinityTask.findRootIndex(true /* effectiveRoot */); if (lastActivityNdx == -1) { lastActivityNdx = 0; } for (int i = numActivities - 1; i > lastActivityNdx; --i) { ActivityRecord target = activities.get(i); if (target.frontOfTask) break; // TODO: Why is this needed? Looks like we're breaking the loop before we reach the root if (target.isRootOfTask()) break; final int flags = target.info.flags; boolean finishOnTaskLaunch = (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; Loading Loading @@ -3590,11 +3596,11 @@ class ActivityStack extends ConfigurationContainer { (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; final TaskRecord task = taskTop.getTaskRecord(); /** False until we evaluate the TaskRecord associated with taskTop. Switches to true * for remaining tasks. Used for later tasks to reparent to task. */ // False until we evaluate the TaskRecord associated with taskTop. Switches to true // for remaining tasks. Used for later tasks to reparent to task. boolean taskFound = false; /** If ActivityOptions are moved out and need to be aborted or moved to taskTop. */ // If ActivityOptions are moved out and need to be aborted or moved to taskTop. ActivityOptions topOptions = null; // Preserve the location for reparenting in the new task. Loading Loading @@ -4009,7 +4015,6 @@ class ActivityStack extends ConfigurationContainer { final ArrayList<ActivityRecord> activities = task.mActivities; final int index = activities.indexOf(r); if (index < (activities.size() - 1)) { task.setFrontOfTask(); if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { // If the caller asked that this activity (and all above it) // be cleared when the task is reset, don't lose that information, Loading Loading @@ -4244,7 +4249,8 @@ class ActivityStack extends ConfigurationContainer { // of a document, unless simply finishing it will return them to the the // correct app behind. final TaskRecord task = srec.getTaskRecord(); if (srec.frontOfTask && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) { if (srec.isRootOfTask() && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) { // Okay, this activity is at the root of its task. What to do, what to do... if (!inFrontOfStandardStack()) { // Finishing won't return to an application, so we need to recreate. Loading services/core/java/com/android/server/wm/ActivityStarter.java +2 −2 Original line number Diff line number Diff line Loading @@ -1557,7 +1557,7 @@ class ActivityStarter { } if (top != null) { if (top.frontOfTask) { if (top.isRootOfTask()) { // Activity aliases may mean we use different intents for the top activity, // so make sure the task now has the identity of the new intent. top.getTaskRecord().setIntent(mStartActivity); Loading Loading @@ -2260,7 +2260,7 @@ class ActivityStarter { || LAUNCH_SINGLE_TOP == mLaunchMode) && intentActivity.mActivityComponent.equals( mStartActivity.mActivityComponent)) { if (intentActivity.frontOfTask) { if (intentActivity.isRootOfTask()) { intentActivity.getTaskRecord().setIntent(mStartActivity); } deliverNewIntent(intentActivity); Loading services/core/java/com/android/server/wm/TaskRecord.java +29 −35 Original line number Diff line number Diff line Loading @@ -1113,17 +1113,15 @@ class TaskRecord extends ConfigurationContainer { return intent != null ? intent : affinityIntent; } /** Returns the first non-finishing activity from the root. */ /** Returns the first non-finishing activity from the bottom. */ ActivityRecord getRootActivity() { for (int i = 0; i < mActivities.size(); i++) { final ActivityRecord r = mActivities.get(i); if (r.finishing) { continue; } return r; } final int rootActivityIndex = findRootIndex(false /* effectiveRoot */); if (rootActivityIndex == -1) { // There are no non-finishing activities in the task. return null; } return mActivities.get(rootActivityIndex); } ActivityRecord getTopActivity() { return getTopActivity(true /* includeOverlays */); Loading Loading @@ -1237,27 +1235,6 @@ class TaskRecord extends ConfigurationContainer { || topRunningActivityLocked() != null; } /** Call after activity movement or finish to make sure that frontOfTask is set correctly */ final void setFrontOfTask() { boolean foundFront = false; final int numActivities = mActivities.size(); for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { final ActivityRecord r = mActivities.get(activityNdx); if (foundFront || r.finishing) { r.frontOfTask = false; } else { r.frontOfTask = true; // Set frontOfTask false for every following activity. foundFront = true; } } if (!foundFront && numActivities > 0) { // All activities of this task are finishing. As we ought to have a frontOfTask // activity, make the bottom activity front. mActivities.get(0).frontOfTask = true; } } /** * Reorder the history stack so that the passed activity is brought to the front. */ Loading @@ -1272,8 +1249,6 @@ class TaskRecord extends ConfigurationContainer { // Make sure window manager is aware of the position change. mTask.positionChildAtTop(newTop.mAppWindowToken); updateEffectiveIntent(); setFrontOfTask(); } void addActivityToTop(ActivityRecord r) { Loading Loading @@ -1658,6 +1633,7 @@ class TaskRecord extends ConfigurationContainer { /** Updates the last task description values. */ void updateTaskDescription() { // TODO(AM refactor): Cleanup to use findRootIndex() // Traverse upwards looking for any break between main task activities and // utility activities. int activityNdx; Loading Loading @@ -1736,8 +1712,19 @@ class TaskRecord extends ConfigurationContainer { } } int findEffectiveRootIndex() { int effectiveNdx = 0; /** * Find the index of the root activity in the task. It will be the first activity from the * bottom that is not finishing. * * @param effectiveRoot Flag indicating whether 'effective root' should be returned - an * activity that defines the task identity (its base intent). It's the * first one that does not have * {@link ActivityInfo#FLAG_RELINQUISH_TASK_IDENTITY}. * @return index of the 'root' or 'effective' root in the list of activities, -1 if no eligible * activity was found. */ int findRootIndex(boolean effectiveRoot) { int effectiveNdx = -1; final int topActivityNdx = mActivities.size() - 1; for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) { final ActivityRecord r = mActivities.get(activityNdx); Loading @@ -1745,15 +1732,22 @@ class TaskRecord extends ConfigurationContainer { continue; } effectiveNdx = activityNdx; if ((r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) { if (!effectiveRoot || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) { break; } } return effectiveNdx; } // TODO (AM refactor): Invoke automatically when there is a change in children @VisibleForTesting void updateEffectiveIntent() { final int effectiveRootIndex = findEffectiveRootIndex(); int effectiveRootIndex = findRootIndex(true /* effectiveRoot */); if (effectiveRootIndex == -1) { // All activities in the task are either finishing or relinquish task identity. // But we still want to update the intent, so let's use the bottom activity. effectiveRootIndex = 0; } final ActivityRecord r = mActivities.get(effectiveRootIndex); setIntent(r); Loading Loading
core/res/res/values/attrs_manifest.xml +5 −1 Original line number Diff line number Diff line Loading @@ -1315,7 +1315,11 @@ it will yield the baseIntent to any activity that it launches in the same task. This continues until an activity is encountered which has this attribute set to false. False is the default. This attribute set to true also permits activity's use of the TaskDescription to change labels, colors and icons in the recent task list. --> TaskDescription to change labels, colors and icons in the recent task list. <p>NOTE: Setting this flag to <code>true</code> will not change the affinity of the task, which is used for intent resolution during activity launch. The task's root activity will always define its affinity. --> <attr name="relinquishTaskIdentity" format="boolean" /> <!-- Indicate that it is okay for this activity be resumed while the previous Loading
services/core/java/com/android/server/wm/ActivityRecord.java +25 −6 Original line number Diff line number Diff line Loading @@ -319,8 +319,6 @@ final class ActivityRecord extends ConfigurationContainer { private ActivityState mState; // current state we are in Bundle icicle; // last saved activity state PersistableBundle persistentState; // last persistently saved activity state // TODO: See if this is still needed. boolean frontOfTask; // is this the root activity of its task? boolean launchFailed; // set if a launched failed, to abort on 2nd try boolean haveState; // have we gotten the last activity state? boolean stopped; // is activity pause finished? Loading Loading @@ -439,7 +437,7 @@ final class ActivityRecord extends ConfigurationContainer { pw.print(" userId="); pw.println(mUserId); pw.print(prefix); pw.print("app="); pw.println(app); pw.print(prefix); pw.println(intent.toInsecureStringWithClip()); pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask); pw.print(prefix); pw.print("rootOfTask="); pw.print(isRootOfTask()); pw.print(" task="); pw.println(task); pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity); pw.print(prefix); pw.print("mActivityComponent="); Loading Loading @@ -957,7 +955,6 @@ final class ActivityRecord extends ConfigurationContainer { resultWho = _resultWho; requestCode = _reqCode; setState(INITIALIZING, "ActivityRecord ctor"); frontOfTask = false; launchFailed = false; stopped = false; delayedResume = false; Loading Loading @@ -2552,7 +2549,8 @@ final class ActivityRecord extends ConfigurationContainer { } final TaskRecord task = r.task; final int activityNdx = task.mActivities.indexOf(r); if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) { if (activityNdx < 0 || (onlyRoot && activityNdx > task.findRootIndex(true /* effectiveRoot */))) { return INVALID_TASK_ID; } return task.taskId; Loading Loading @@ -3803,6 +3801,27 @@ final class ActivityRecord extends ConfigurationContainer { return display != null && this == display.getResumedActivity(); } /** * Check if this is the root of the task - first activity that is not finishing, starting from * the bottom of the task. If all activities are finishing - then this method will return * {@code true} if the activity is at the bottom. * * NOTE: This is different from 'effective root' - an activity that defines the task identity. */ boolean isRootOfTask() { if (task == null) { return false; } final ActivityRecord rootActivity = task.getRootActivity(); if (rootActivity != null) { return this == rootActivity; } // No non-finishing activity found. In this case the bottom-most activity is considered to // be the root. return task.getChildAt(0) == this; } void registerRemoteAnimations(RemoteAnimationDefinition definition) { if (mAppWindowToken == null) { Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app" Loading Loading @@ -3846,7 +3865,7 @@ final class ActivityRecord extends ConfigurationContainer { writeIdentifierToProto(proto, IDENTIFIER); proto.write(STATE, mState.toString()); proto.write(VISIBLE, visible); proto.write(FRONT_OF_TASK, frontOfTask); proto.write(FRONT_OF_TASK, isRootOfTask()); if (hasProcess()) { proto.write(PROC_ID, app.getPid()); } Loading
services/core/java/com/android/server/wm/ActivityStack.java +37 −31 Original line number Diff line number Diff line Loading @@ -1895,7 +1895,7 @@ class ActivityStack extends ConfigurationContainer { // last of activity of the last task the stack will be empty and must // be cleared immediately. boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE || (r.frontOfTask && mTaskHistory.size() <= 1); || (r.isRootOfTask() && mTaskHistory.size() <= 1); if (scheduleIdle || forceIdle) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle=" + forceIdle + "immediate=" + !idleDelayed); Loading Loading @@ -3209,8 +3209,6 @@ class ActivityStack extends ConfigurationContainer { r.createAppWindowToken(); } task.setFrontOfTask(); // The transition animation and starting window are not needed if {@code allowMoveToFront} // is false, because the activity won't be visible. if ((!isHomeOrRecentsStack() || numActivities() > 0) && allowMoveToFront) { Loading Loading @@ -3322,20 +3320,17 @@ class ActivityStack extends ConfigurationContainer { } /** * Perform a reset of the given task, if needed as part of launching it. * Returns the new HistoryRecord at the top of the task. */ /** * Helper method for #resetTaskIfNeededLocked. * We are inside of the task being reset... we'll either finish this activity, push it out * for another task, or leave it as-is. * Helper method for {@link #resetTaskIfNeededLocked(ActivityRecord, ActivityRecord)}. * Performs a reset of the given task, if needed for new activity start. * @param task The task containing the Activity (taskTop) that might be reset. * @param forceReset * @param forceReset Flag indicating if clear task was requested * @return An ActivityOptions that needs to be processed. */ private ActivityOptions resetTargetTaskIfNeededLocked(TaskRecord task, boolean forceReset) { ActivityOptions topOptions = null; // Tracker of the end of currently handled reply chain (sublist) of activities. What happens // to activities in the same chain will depend on what the end activity of the chain needs. int replyChainEnd = -1; boolean canMoveOptions = true; Loading @@ -3343,11 +3338,14 @@ class ActivityStack extends ConfigurationContainer { // the root, we may no longer have the task!). final ArrayList<ActivityRecord> activities = task.mActivities; final int numActivities = activities.size(); final int rootActivityNdx = task.findEffectiveRootIndex(); for (int i = numActivities - 1; i > rootActivityNdx; --i ) { int lastActivityNdx = task.findRootIndex(true /* effectiveRoot */); if (lastActivityNdx == -1) { lastActivityNdx = 0; } for (int i = numActivities - 1; i > lastActivityNdx; --i) { ActivityRecord target = activities.get(i); if (target.frontOfTask) break; // TODO: Why is this needed? Looks like we're breaking the loop before we reach the root if (target.isRootOfTask()) break; final int flags = target.info.flags; final boolean finishOnTaskLaunch = Loading Loading @@ -3380,12 +3378,13 @@ class ActivityStack extends ConfigurationContainer { // bottom of the activity stack. This also keeps it // correctly ordered with any activities we previously // moved. // TODO: We should probably look for other stacks also, since corresponding task // with the same affinity is unlikely to be in the same stack. final TaskRecord targetTask; final ActivityRecord bottom = !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ? mTaskHistory.get(0).mActivities.get(0) : null; if (bottom != null && target.taskAffinity != null && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) { if (bottom != null && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) { // If the activity currently at the bottom has the // same task affinity as the one we are moving, // then merge it into the same task. Loading @@ -3395,7 +3394,8 @@ class ActivityStack extends ConfigurationContainer { } else { targetTask = createTaskRecord( mStackSupervisor.getNextTaskIdForUserLocked(target.mUserId), target.info, null, null, null, false); target.info, null /* intent */, null /* voiceSession */, null /* voiceInteractor */, false /* toTop */); targetTask.affinityIntent = target.intent; if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target + " out to new task " + targetTask); Loading Loading @@ -3476,28 +3476,34 @@ class ActivityStack extends ConfigurationContainer { } /** * Helper method for #resetTaskIfNeededLocked. Processes all of the activities in a given * TaskRecord looking for an affinity with the task of resetTaskIfNeededLocked.taskTop. * Helper method for {@link #resetTaskIfNeededLocked(ActivityRecord, ActivityRecord)}. * Processes all of the activities in a given TaskRecord looking for an affinity with the task * of resetTaskIfNeededLocked.taskTop. * @param affinityTask The task we are looking for an affinity to. * @param task Task that resetTaskIfNeededLocked.taskTop belongs to. * @param topTaskIsHigher True if #task has already been processed by resetTaskIfNeededLocked. * @param forceReset Flag passed in to resetTaskIfNeededLocked. * @param forceReset Flag indicating if clear task was requested */ // TODO: Consider merging with #resetTargetTaskIfNeededLocked() above private int resetAffinityTaskIfNeededLocked(TaskRecord affinityTask, TaskRecord task, boolean topTaskIsHigher, boolean forceReset, int taskInsertionPoint) { // Tracker of the end of currently handled reply chain (sublist) of activities. What happens // to activities in the same chain will depend on what the end activity of the chain needs. int replyChainEnd = -1; final int taskId = task.taskId; final String taskAffinity = task.affinity; final ArrayList<ActivityRecord> activities = affinityTask.mActivities; final int numActivities = activities.size(); final int rootActivityNdx = affinityTask.findEffectiveRootIndex(); // Do not operate on or below the effective root Activity. for (int i = numActivities - 1; i > rootActivityNdx; --i) { int lastActivityNdx = affinityTask.findRootIndex(true /* effectiveRoot */); if (lastActivityNdx == -1) { lastActivityNdx = 0; } for (int i = numActivities - 1; i > lastActivityNdx; --i) { ActivityRecord target = activities.get(i); if (target.frontOfTask) break; // TODO: Why is this needed? Looks like we're breaking the loop before we reach the root if (target.isRootOfTask()) break; final int flags = target.info.flags; boolean finishOnTaskLaunch = (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; Loading Loading @@ -3590,11 +3596,11 @@ class ActivityStack extends ConfigurationContainer { (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; final TaskRecord task = taskTop.getTaskRecord(); /** False until we evaluate the TaskRecord associated with taskTop. Switches to true * for remaining tasks. Used for later tasks to reparent to task. */ // False until we evaluate the TaskRecord associated with taskTop. Switches to true // for remaining tasks. Used for later tasks to reparent to task. boolean taskFound = false; /** If ActivityOptions are moved out and need to be aborted or moved to taskTop. */ // If ActivityOptions are moved out and need to be aborted or moved to taskTop. ActivityOptions topOptions = null; // Preserve the location for reparenting in the new task. Loading Loading @@ -4009,7 +4015,6 @@ class ActivityStack extends ConfigurationContainer { final ArrayList<ActivityRecord> activities = task.mActivities; final int index = activities.indexOf(r); if (index < (activities.size() - 1)) { task.setFrontOfTask(); if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { // If the caller asked that this activity (and all above it) // be cleared when the task is reset, don't lose that information, Loading Loading @@ -4244,7 +4249,8 @@ class ActivityStack extends ConfigurationContainer { // of a document, unless simply finishing it will return them to the the // correct app behind. final TaskRecord task = srec.getTaskRecord(); if (srec.frontOfTask && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) { if (srec.isRootOfTask() && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) { // Okay, this activity is at the root of its task. What to do, what to do... if (!inFrontOfStandardStack()) { // Finishing won't return to an application, so we need to recreate. Loading
services/core/java/com/android/server/wm/ActivityStarter.java +2 −2 Original line number Diff line number Diff line Loading @@ -1557,7 +1557,7 @@ class ActivityStarter { } if (top != null) { if (top.frontOfTask) { if (top.isRootOfTask()) { // Activity aliases may mean we use different intents for the top activity, // so make sure the task now has the identity of the new intent. top.getTaskRecord().setIntent(mStartActivity); Loading Loading @@ -2260,7 +2260,7 @@ class ActivityStarter { || LAUNCH_SINGLE_TOP == mLaunchMode) && intentActivity.mActivityComponent.equals( mStartActivity.mActivityComponent)) { if (intentActivity.frontOfTask) { if (intentActivity.isRootOfTask()) { intentActivity.getTaskRecord().setIntent(mStartActivity); } deliverNewIntent(intentActivity); Loading
services/core/java/com/android/server/wm/TaskRecord.java +29 −35 Original line number Diff line number Diff line Loading @@ -1113,17 +1113,15 @@ class TaskRecord extends ConfigurationContainer { return intent != null ? intent : affinityIntent; } /** Returns the first non-finishing activity from the root. */ /** Returns the first non-finishing activity from the bottom. */ ActivityRecord getRootActivity() { for (int i = 0; i < mActivities.size(); i++) { final ActivityRecord r = mActivities.get(i); if (r.finishing) { continue; } return r; } final int rootActivityIndex = findRootIndex(false /* effectiveRoot */); if (rootActivityIndex == -1) { // There are no non-finishing activities in the task. return null; } return mActivities.get(rootActivityIndex); } ActivityRecord getTopActivity() { return getTopActivity(true /* includeOverlays */); Loading Loading @@ -1237,27 +1235,6 @@ class TaskRecord extends ConfigurationContainer { || topRunningActivityLocked() != null; } /** Call after activity movement or finish to make sure that frontOfTask is set correctly */ final void setFrontOfTask() { boolean foundFront = false; final int numActivities = mActivities.size(); for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { final ActivityRecord r = mActivities.get(activityNdx); if (foundFront || r.finishing) { r.frontOfTask = false; } else { r.frontOfTask = true; // Set frontOfTask false for every following activity. foundFront = true; } } if (!foundFront && numActivities > 0) { // All activities of this task are finishing. As we ought to have a frontOfTask // activity, make the bottom activity front. mActivities.get(0).frontOfTask = true; } } /** * Reorder the history stack so that the passed activity is brought to the front. */ Loading @@ -1272,8 +1249,6 @@ class TaskRecord extends ConfigurationContainer { // Make sure window manager is aware of the position change. mTask.positionChildAtTop(newTop.mAppWindowToken); updateEffectiveIntent(); setFrontOfTask(); } void addActivityToTop(ActivityRecord r) { Loading Loading @@ -1658,6 +1633,7 @@ class TaskRecord extends ConfigurationContainer { /** Updates the last task description values. */ void updateTaskDescription() { // TODO(AM refactor): Cleanup to use findRootIndex() // Traverse upwards looking for any break between main task activities and // utility activities. int activityNdx; Loading Loading @@ -1736,8 +1712,19 @@ class TaskRecord extends ConfigurationContainer { } } int findEffectiveRootIndex() { int effectiveNdx = 0; /** * Find the index of the root activity in the task. It will be the first activity from the * bottom that is not finishing. * * @param effectiveRoot Flag indicating whether 'effective root' should be returned - an * activity that defines the task identity (its base intent). It's the * first one that does not have * {@link ActivityInfo#FLAG_RELINQUISH_TASK_IDENTITY}. * @return index of the 'root' or 'effective' root in the list of activities, -1 if no eligible * activity was found. */ int findRootIndex(boolean effectiveRoot) { int effectiveNdx = -1; final int topActivityNdx = mActivities.size() - 1; for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) { final ActivityRecord r = mActivities.get(activityNdx); Loading @@ -1745,15 +1732,22 @@ class TaskRecord extends ConfigurationContainer { continue; } effectiveNdx = activityNdx; if ((r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) { if (!effectiveRoot || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) { break; } } return effectiveNdx; } // TODO (AM refactor): Invoke automatically when there is a change in children @VisibleForTesting void updateEffectiveIntent() { final int effectiveRootIndex = findEffectiveRootIndex(); int effectiveRootIndex = findRootIndex(true /* effectiveRoot */); if (effectiveRootIndex == -1) { // All activities in the task are either finishing or relinquish task identity. // But we still want to update the intent, so let's use the bottom activity. effectiveRootIndex = 0; } final ActivityRecord r = mActivities.get(effectiveRootIndex); setIntent(r); Loading