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() {