Merge "Fix issue with task overlay activities not finishing."
diff --git a/api/test-current.txt b/api/test-current.txt
index 4d9c6d3..8b7a42a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4031,6 +4031,8 @@
method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
method public android.app.ActivityOptions setLaunchDisplayId(int);
method public void setLaunchStackId(int);
+ method public void setLaunchTaskId(int);
+ method public void setTaskOverlay(boolean, boolean);
method public android.os.Bundle toBundle();
method public void update(android.app.ActivityOptions);
field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 0b9479d..aa7cdf7 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1068,6 +1068,7 @@
* Sets the task the activity will be launched in.
* @hide
*/
+ @TestApi
public void setLaunchTaskId(int taskId) {
mLaunchTaskId = taskId;
}
@@ -1085,6 +1086,7 @@
* the task will also not be moved to the front of the stack.
* @hide
*/
+ @TestApi
public void setTaskOverlay(boolean taskOverlay, boolean canResume) {
mTaskOverlay = taskOverlay;
mTaskOverlayCanResume = canResume;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 6bfab78..2519e02 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -113,6 +113,7 @@
<uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="android.permission.MANAGE_AUTO_FILL" />
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index aaad12c..9b6d13a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -82,6 +82,8 @@
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+
final class ActivityManagerShellCommand extends ShellCommand {
public static final String NO_CLASS_ERROR_CODE = "Error type 3";
private static final String SHELL_PACKAGE_NAME = "com.android.shell";
@@ -118,6 +120,8 @@
private boolean mStreaming; // Streaming the profiling output to a file.
private int mDisplayId;
private int mStackId;
+ private int mTaskId;
+ private boolean mIsTaskOverlay;
final boolean mDumping;
@@ -263,6 +267,8 @@
mUserId = defUser;
mDisplayId = INVALID_DISPLAY;
mStackId = INVALID_STACK_ID;
+ mTaskId = INVALID_TASK_ID;
+ mIsTaskOverlay = false;
return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
@Override
@@ -297,6 +303,10 @@
mDisplayId = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("--stack")) {
mStackId = Integer.parseInt(getNextArgRequired());
+ } else if (opt.equals("--task")) {
+ mTaskId = Integer.parseInt(getNextArgRequired());
+ } else if (opt.equals("--task-overlay")) {
+ mIsTaskOverlay = true;
} else {
return false;
}
@@ -380,6 +390,14 @@
options = ActivityOptions.makeBasic();
options.setLaunchStackId(mStackId);
}
+ if (mTaskId != INVALID_TASK_ID) {
+ options = ActivityOptions.makeBasic();
+ options.setLaunchTaskId(mTaskId);
+
+ if (mIsTaskOverlay) {
+ options.setTaskOverlay(true, true /* canResume */);
+ }
+ }
if (mWaitOption) {
result = mInterface.startActivityAndWait(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 28a4e1a..498de63 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -67,7 +67,9 @@
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_NONE;
@@ -121,7 +123,6 @@
import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
import com.android.server.wm.StackWindowController;
import com.android.server.wm.StackWindowListener;
-import com.android.server.wm.TaskStack;
import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
@@ -1185,13 +1186,13 @@
* @param resuming The activity we are currently trying to resume or null if this is not being
* called as part of resuming the top activity, so we shouldn't try to instigate
* a resume here if not null.
- * @param dontWait True if the caller does not want to wait for the pause to complete. If
- * set to true, we will immediately complete the pause here before returning.
+ * @param pauseImmediately True if the caller does not want to wait for the activity callback to
+ * complete pausing.
* @return Returns true if an activity now is in the PAUSING state, and we are waiting for
* it to tell us when it is done.
*/
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
- ActivityRecord resuming, boolean dontWait) {
+ ActivityRecord resuming, boolean pauseImmediately) {
if (mPausingActivity != null) {
Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ " state=" + mPausingActivity.state);
@@ -1213,7 +1214,8 @@
if (mActivityContainer.mParentActivity == null) {
// Top level stack, not a child. Look for child stacks.
- mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming, dontWait);
+ mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming,
+ pauseImmediately);
}
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSING: " + prev);
@@ -1243,7 +1245,7 @@
prev.shortComponentName);
mService.updateUsageStats(prev, false);
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
- userLeaving, prev.configChangeFlags, dontWait);
+ userLeaving, prev.configChangeFlags, pauseImmediately);
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
@@ -1274,7 +1276,7 @@
Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off");
}
- if (dontWait) {
+ if (pauseImmediately) {
// If the caller said they don't want to wait for the pause, then complete
// the pause now.
completePauseLocked(false, resuming);
@@ -3488,11 +3490,19 @@
}
/**
+ * See {@link #finishActivityLocked(ActivityRecord, int, Intent, String, boolean, boolean)}
+ */
+ final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
+ String reason, boolean oomAdj) {
+ return finishActivityLocked(r, resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
+ }
+
+ /**
* @return Returns true if this activity has been removed from the history
* list, or false if it is still in the list and will be removed later.
*/
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
- String reason, boolean oomAdj) {
+ String reason, boolean oomAdj, boolean pauseImmediately) {
if (r.finishing) {
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
@@ -3529,9 +3539,10 @@
if (mResumedActivity == r) {
if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare close transition: finishing " + r);
- if (endTask) {
- mService.mTaskChangeNotificationController.notifyTaskRemovalStarted(task.taskId);
- }
+ if (endTask) {
+ mService.mTaskChangeNotificationController.notifyTaskRemovalStarted(
+ task.taskId);
+ }
mWindowManager.prepareAppTransition(transit, false);
// Tell window manager to prepare for this one to be removed.
@@ -3541,7 +3552,7 @@
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish needs to pause: " + r);
if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
"finish() => pause with userLeaving=false");
- startPausingLocked(false, false, null, false);
+ startPausingLocked(false, false, null, pauseImmediately);
}
if (endTask) {
@@ -3552,15 +3563,30 @@
// it is done pausing; else we can just directly finish it here.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + r);
if (r.visible) {
- mWindowManager.prepareAppTransition(transit, false);
- r.setVisibility(false);
- mWindowManager.executeAppTransition();
- if (!mStackSupervisor.mWaitingVisibleActivities.contains(r)) {
- mStackSupervisor.mWaitingVisibleActivities.add(r);
+ prepareActivityHideTransitionAnimation(r, transit);
+ }
+
+ final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE
+ : FINISH_AFTER_PAUSE;
+ final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj)
+ == null;
+
+ // The following code is an optimization. When the last non-task overlay activity
+ // is removed from the task, we remove the entire task from the stack. However,
+ // since that is done after the scheduled destroy callback from the activity, that
+ // call to change the visibility of the task overlay activities would be out of
+ // sync with the activitiy visibility being set for this finishing activity above.
+ // In this case, we can set the visibility of all the task overlay activities when
+ // we detect the last one is finishing to keep them in sync.
+ if (task.onlyHasTaskOverlayActivities(true /* excludeFinishing */)) {
+ for (ActivityRecord taskOverlay : task.mActivities) {
+ if (!taskOverlay.mTaskOverlay) {
+ continue;
+ }
+ prepareActivityHideTransitionAnimation(taskOverlay, transit);
}
}
- return finishCurrentActivityLocked(r, (r.visible || r.nowVisible) ?
- FINISH_AFTER_VISIBLE : FINISH_AFTER_PAUSE, oomAdj) == null;
+ return removedActivity;
} else {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + r);
}
@@ -3571,6 +3597,15 @@
}
}
+ private void prepareActivityHideTransitionAnimation(ActivityRecord r, int transit) {
+ mWindowManager.prepareAppTransition(transit, false);
+ r.setVisibility(false);
+ mWindowManager.executeAppTransition();
+ if (!mStackSupervisor.mWaitingVisibleActivities.contains(r)) {
+ mStackSupervisor.mWaitingVisibleActivities.add(r);
+ }
+ }
+
static final int FINISH_IMMEDIATELY = 0;
static final int FINISH_AFTER_PAUSE = 1;
static final int FINISH_AFTER_VISIBLE = 2;
@@ -3864,15 +3899,23 @@
r.removeWindowContainer();
final TaskRecord task = r.task;
final boolean lastActivity = task != null ? task.removeActivity(r) : false;
+ // If we are removing the last activity in the task, not including task overlay activities,
+ // then fall through into the block below to remove the entire task itself
+ final boolean onlyHasTaskOverlays = task != null
+ ? task.onlyHasTaskOverlayActivities(false /* excludingFinishing */) : false;
- if (lastActivity) {
- if (DEBUG_STACK) Slog.i(TAG_STACK,
- "removeActivityFromHistoryLocked: last activity removed from " + this);
+ if (lastActivity || onlyHasTaskOverlays) {
+ if (DEBUG_STACK) {
+ Slog.i(TAG_STACK,
+ "removeActivityFromHistoryLocked: last activity removed from " + this
+ + " onlyHasTaskOverlays=" + onlyHasTaskOverlays);
+ }
+
if (mStackSupervisor.isFocusedStack(this) && task == topTask() &&
task.isOverHomeStack()) {
mStackSupervisor.moveHomeStackTaskToTop(reason);
}
- removeTask(task, reason);
+ removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
}
cleanUpActivityServicesLocked(r);
r.removeUriPermissionsLocked();
@@ -4923,10 +4966,6 @@
return starting;
}
- void removeTask(TaskRecord task, String reason) {
- removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
- }
-
/**
* Removes the input task from this stack.
* @param task to remove.
@@ -4936,6 +4975,10 @@
*/
void removeTask(TaskRecord task, String reason, int mode) {
if (mode == REMOVE_TASK_MODE_DESTROYING) {
+ // When destroying a task, tell the supervisor to remove it so that any activity it has
+ // can be cleaned up correctly
+ mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
+ !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY);
task.removeWindowContainer();
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8559dca..b623b2f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -254,6 +254,10 @@
// Used to indicate that a task is removed it should also be removed from recents.
static final boolean REMOVE_FROM_RECENTS = true;
+ // Used to indicate that pausing an activity should occur immediately without waiting for
+ // the activity callback indicating that it has completed pausing
+ static final boolean PAUSE_IMMEDIATELY = true;
+
/**
* The modes which affect which tasks are returned when calling
* {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
@@ -2536,18 +2540,28 @@
}
/**
+ * See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)}
+ */
+ boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
+ return removeTaskByIdLocked(taskId, killProcess, removeFromRecents, !PAUSE_IMMEDIATELY);
+ }
+
+ /**
* Removes the task with the specified task id.
*
* @param taskId Identifier of the task to be removed.
* @param killProcess Kill any process associated with the task if possible.
* @param removeFromRecents Whether to also remove the task from recents.
+ * @param pauseImmediately Pauses all task activities immediately without waiting for the
+ * pause-complete callback from the activity.
* @return Returns true if the given task was found and removed.
*/
- boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
+ boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
+ boolean pauseImmediately) {
final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
INVALID_STACK_ID);
if (tr != null) {
- tr.removeTaskActivitiesLocked();
+ tr.removeTaskActivitiesLocked(pauseImmediately);
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
if (tr.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index bff5f51..ce32f84 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -112,6 +112,7 @@
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
+import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static java.lang.Integer.MAX_VALUE;
@@ -1280,6 +1281,26 @@
return false;
}
+ /**
+ * @return whether or not there are ONLY task overlay activities in the stack.
+ * If {@param excludeFinishing} is set, then ignore finishing activities in the check.
+ * If there are no task overlay activities, this call returns false.
+ */
+ boolean onlyHasTaskOverlayActivities(boolean excludeFinishing) {
+ int count = 0;
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = mActivities.get(i);
+ if (excludeFinishing && r.finishing) {
+ continue;
+ }
+ if (!r.mTaskOverlay) {
+ return false;
+ }
+ count++;
+ }
+ return count > 0;
+ }
+
boolean autoRemoveFromRecents() {
// We will automatically remove the task either if it has explicitly asked for
// this, or it is empty and has never contained an activity that got shown to
@@ -1291,7 +1312,7 @@
* Completely remove all activities associated with an existing
* task starting at a specified index.
*/
- final void performClearTaskAtIndexLocked(int activityNdx) {
+ final void performClearTaskAtIndexLocked(int activityNdx, boolean pauseImmediately) {
int numActivities = mActivities.size();
for ( ; activityNdx < numActivities; ++activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
@@ -1304,8 +1325,8 @@
mActivities.remove(activityNdx);
--activityNdx;
--numActivities;
- } else if (mStack.finishActivityLocked(
- r, Activity.RESULT_CANCELED, null, "clear-task-index", false)) {
+ } else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null,
+ "clear-task-index", false, pauseImmediately)) {
--activityNdx;
--numActivities;
}
@@ -1317,7 +1338,7 @@
*/
final void performClearTaskLocked() {
mReuseTask = true;
- performClearTaskAtIndexLocked(0);
+ performClearTaskAtIndexLocked(0, !PAUSE_IMMEDIATELY);
mReuseTask = false;
}
@@ -1401,9 +1422,9 @@
return taskThumbnail;
}
- void removeTaskActivitiesLocked() {
+ void removeTaskActivitiesLocked(boolean pauseImmediately) {
// Just remove the entire task.
- performClearTaskAtIndexLocked(0);
+ performClearTaskAtIndexLocked(0, pauseImmediately);
}
String lockTaskAuthToString() {