Piping through ability for an Activity to remove its own task. (Bug 13735914)
Change-Id: Iefcd4fbe68748195de8ee37ee2b6edef55276603
diff --git a/api/current.txt b/api/current.txt
index 4e2a3bb..59aac1e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3175,6 +3175,7 @@
method public void finishActivity(int);
method public void finishActivityFromChild(android.app.Activity, int);
method public void finishAffinity();
+ method public void finishAndRemoveTask();
method public void finishFromChild(android.app.Activity);
method public void finishWithTransition();
method public android.app.ActionBar getActionBar();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a5a06e3..599a608 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4314,11 +4314,10 @@
}
/**
- * Call this when your activity is done and should be closed. The
- * ActivityResult is propagated back to whoever launched you via
- * onActivityResult().
+ * Finishes the current activity and specifies whether to remove the task associated with this
+ * activity.
*/
- public void finish() {
+ private void finish(boolean finishTask) {
if (mParent == null) {
int resultCode;
Intent resultData;
@@ -4332,7 +4331,7 @@
resultData.prepareToLeaveProcess();
}
if (ActivityManagerNative.getDefault()
- .finishActivity(mToken, resultCode, resultData)) {
+ .finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
} catch (RemoteException e) {
@@ -4344,6 +4343,15 @@
}
/**
+ * Call this when your activity is done and should be closed. The
+ * ActivityResult is propagated back to whoever launched you via
+ * onActivityResult().
+ */
+ public void finish() {
+ finish(false);
+ }
+
+ /**
* Finish this activity as well as all activities immediately below it
* in the current task that have the same affinity. This is typically
* used when an application can be launched on to another task (such as
@@ -4442,6 +4450,14 @@
}
/**
+ * Call this when your activity is done and should be closed and the task should be completely
+ * removed as a part of finishing the Activity.
+ */
+ public void finishAndRemoveTask() {
+ finish(true);
+ }
+
+ /**
* Called when an activity you launched exits, giving you the requestCode
* you started it with, the resultCode it returned, and any additional
* data from it. The <var>resultCode</var> will be
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 44c74d8..10831f2 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -263,7 +263,8 @@
if (data.readInt() != 0) {
resultData = Intent.CREATOR.createFromParcel(data);
}
- boolean res = finishActivity(token, resultCode, resultData);
+ boolean finishTask = (data.readInt() != 0);
+ boolean res = finishActivity(token, resultCode, resultData, finishTask);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
@@ -2342,7 +2343,7 @@
data.recycle();
return result != 0;
}
- public boolean finishActivity(IBinder token, int resultCode, Intent resultData)
+ public boolean finishActivity(IBinder token, int resultCode, Intent resultData, boolean finishTask)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2355,6 +2356,7 @@
} else {
data.writeInt(0);
}
+ data.writeInt(finishTask ? 1 : 0);
mRemote.transact(FINISH_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 965f815..88eae7f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2363,7 +2363,7 @@
// manager to stop us.
try {
ActivityManagerNative.getDefault()
- .finishActivity(r.token, Activity.RESULT_CANCELED, null);
+ .finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
// Ignore
}
@@ -2984,7 +2984,7 @@
// just end this activity.
try {
ActivityManagerNative.getDefault()
- .finishActivity(token, Activity.RESULT_CANCELED, null);
+ .finishActivity(token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index bfbd339..52003f1 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -79,7 +79,7 @@
int flagsMask, int flagsValues, Bundle options) throws RemoteException;
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle options) throws RemoteException;
- public boolean finishActivity(IBinder token, int code, Intent data)
+ public boolean finishActivity(IBinder token, int code, Intent data, boolean finishTask)
throws RemoteException;
public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
public boolean finishActivityAffinity(IBinder token) throws RemoteException;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ecbb0d9..8d1a24a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3417,11 +3417,14 @@
* @param token The Binder token referencing the Activity we want to finish.
* @param resultCode Result code, if any, from this Activity.
* @param resultData Result data (Intent), if any, from this Activity.
+ * @param finishTask Whether to finish the task associated with this Activity. Only applies to
+ * the root Activity in the task.
*
* @return Returns true if the activity successfully finished, or false if it is still running.
*/
@Override
- public final boolean finishActivity(IBinder token, int resultCode, Intent resultData) {
+ public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
+ boolean finishTask) {
// Refuse possible leaked file descriptors
if (resultData != null && resultData.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -3432,6 +3435,9 @@
if (r == null) {
return true;
}
+ // Keep track of the root activity of the task before we finish it
+ TaskRecord tr = r.task;
+ ActivityRecord rootR = tr.getRootActivity();
if (mController != null) {
// Find the first activity that is not finishing.
ActivityRecord next = r.task.stack.topRunningActivityLocked(token, 0);
@@ -3451,10 +3457,21 @@
}
}
final long origId = Binder.clearCallingIdentity();
- boolean res = r.task.stack.requestFinishActivityLocked(token, resultCode,
- resultData, "app-request", true);
- Binder.restoreCallingIdentity(origId);
- return res;
+ try {
+ boolean res;
+ if (finishTask && r == rootR) {
+ // If requested, remove the task that is associated to this activity only if it
+ // was the root activity in the task. The result code and data is ignored because
+ // we don't support returning them across task boundaries.
+ res = removeTaskByIdLocked(tr.taskId, 0);
+ } else {
+ res = tr.stack.requestFinishActivityLocked(token, resultCode,
+ resultData, "app-request", true);
+ }
+ return res;
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
}
@@ -7084,6 +7101,24 @@
}
}
+ /**
+ * Removes the task with the specified task id.
+ *
+ * @param taskId Identifier of the task to be removed.
+ * @param flags Additional operational flags. May be 0 or
+ * {@link ActivityManager#REMOVE_TASK_KILL_PROCESS}.
+ * @return Returns true if the given task was found and removed.
+ */
+ private boolean removeTaskByIdLocked(int taskId, int flags) {
+ TaskRecord tr = recentTaskForIdLocked(taskId);
+ if (tr != null) {
+ tr.removeTaskActivitiesLocked(-1, false);
+ cleanUpRemovedTaskLocked(tr, flags);
+ return true;
+ }
+ return false;
+ }
+
@Override
public boolean removeTask(int taskId, int flags) {
synchronized (this) {
@@ -7091,29 +7126,11 @@
"removeTask()");
long ident = Binder.clearCallingIdentity();
try {
- TaskRecord tr = recentTaskForIdLocked(taskId);
- if (tr != null) {
- ActivityRecord r = tr.removeTaskActivitiesLocked(-1, false);
- if (r != null) {
- cleanUpRemovedTaskLocked(tr, flags);
- return true;
- }
- if (tr.mActivities.size() == 0) {
- // Caller is just removing a recent task that is
- // not actively running. That is easy!
- cleanUpRemovedTaskLocked(tr, flags);
- return true;
- }
- Slog.w(TAG, "removeTask: task " + taskId
- + " does not have activities to remove, "
- + " but numActivities=" + tr.numActivities
- + ": " + tr);
- }
+ return removeTaskByIdLocked(taskId, flags);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- return false;
}
/**
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 3a43521..80a219dd 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -139,6 +139,18 @@
}
}
+ /** Returns the first non-finishing activity from the root. */
+ ActivityRecord getRootActivity() {
+ for (int i = 0; i < mActivities.size(); i++) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ return r;
+ }
+ return null;
+ }
+
ActivityRecord getTopActivity() {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
@@ -305,7 +317,7 @@
}
public ActivityManager.TaskThumbnails getTaskThumbnailsLocked() {
- TaskAccessInfo info = getTaskAccessInfoLocked(true);
+ TaskAccessInfo info = getTaskAccessInfoLocked();
final ActivityRecord resumedActivity = stack.mResumedActivity;
if (resumedActivity != null && resumedActivity.thumbHolder == this) {
info.mainThumbnail = stack.screenshotActivities(resumedActivity);
@@ -325,7 +337,7 @@
}
// Return the information about the task, to figure out the top
// thumbnail to return.
- TaskAccessInfo info = getTaskAccessInfoLocked(true);
+ TaskAccessInfo info = getTaskAccessInfoLocked();
if (info.numSubThumbbails <= 0) {
return info.mainThumbnail != null ? info.mainThumbnail : lastThumbnail;
}
@@ -334,7 +346,7 @@
public ActivityRecord removeTaskActivitiesLocked(int subTaskIndex,
boolean taskRequired) {
- TaskAccessInfo info = getTaskAccessInfoLocked(false);
+ TaskAccessInfo info = getTaskAccessInfoLocked();
if (info.root == null) {
if (taskRequired) {
Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId);
@@ -369,7 +381,7 @@
return mTaskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
}
- public TaskAccessInfo getTaskAccessInfoLocked(boolean inclThumbs) {
+ public TaskAccessInfo getTaskAccessInfoLocked() {
final TaskAccessInfo thumbs = new TaskAccessInfo();
// How many different sub-thumbnails?
final int NA = mActivities.size();