diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index fb70098d69597..342155d72674a 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -284,6 +284,17 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case START_ACTIVITY_FROM_RECENTS_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ int taskId = data.readInt();
+ Bundle options = data.readInt() == 0 ? null : Bundle.CREATOR.createFromParcel(data);
+ int result = startActivityFromRecents(taskId, options);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
case FINISH_ACTIVITY_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -2482,6 +2493,24 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
return result != 0;
}
+ public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(taskId);
+ if (options == null) {
+ data.writeInt(0);
+ } else {
+ data.writeInt(1);
+ options.writeToParcel(data, 0);
+ }
+ mRemote.transact(START_ACTIVITY_FROM_RECENTS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
public boolean finishActivity(IBinder token, int resultCode, Intent resultData, boolean finishTask)
throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index ac2916124ef3a..cc13a3b364f21 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -86,6 +86,7 @@ public interface IActivityManager extends IInterface {
ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException;
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle options) throws RemoteException;
+ public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException;
public boolean finishActivity(IBinder token, int code, Intent data, boolean finishTask)
throws RemoteException;
public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
@@ -756,4 +757,5 @@ public interface IActivityManager extends IInterface {
int IS_BG_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+226;
int MEDIA_RESOURCES_RELEASED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+227;
int NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+228;
+ int START_ACTIVITY_FROM_RECENTS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 229;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7f97726a6ff21..d4a3b126585e3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1364,6 +1364,14 @@
android:label="@string/permlab_getTasks"
android:description="@string/permdesc_getTasks" />
+
+
+
+ start a task from recents
+
+ Allows the app to use an ActivityManager.RecentTaskInfo
+ object to launch a defunct task that was returned from ActivityManager.getRecentTaskList().
+
interact across users
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a0388990b018e..deff19c8cf40c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -74,6 +74,7 @@
+
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 5fadc71bf6fd5..3b436f04ed36c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -467,4 +467,13 @@ public class SystemServicesProxy {
public Bitmap takeAppScreenshot() {
return takeScreenshot();
}
+
+ public void startActivityFromRecents(int taskId, ActivityOptions options) {
+ if (mIam != null) {
+ try {
+ mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle());
+ } catch (RemoteException e) {
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index e1c652e69377c..b6479db8c66b4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -27,7 +27,6 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -452,8 +451,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public void run() {
if (task.isActive) {
// Bring an active task to the foreground
- RecentsTaskLoader.getInstance().getSystemServicesProxy()
- .moveTaskToFront(task.key.id, launchOpts);
+ ssp.moveTaskToFront(task.key.id, launchOpts);
} else {
// Launch the activity anew with the desired animation
Intent i = new Intent(task.key.baseIntent);
@@ -463,15 +461,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
try {
- UserHandle taskUser = new UserHandle(task.userId);
- if (launchOpts != null) {
- getContext().startActivityAsUser(i, launchOpts.toBundle(), taskUser);
-
- } else {
- getContext().startActivityAsUser(i, taskUser);
- if (lockToTask) {
- ssp.lockCurrentTask();
- }
+ ssp.startActivityFromRecents(task.key.id, launchOpts);
+ if (launchOpts == null && lockToTask) {
+ ssp.lockCurrentTask();
}
} catch (ActivityNotFoundException anfe) {
Console.logError(getContext(), "Could not start Activity");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3aaa4021b399b..2d6d09b70e223 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -3545,6 +3546,23 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
+ public final int startActivityFromRecents(int taskId, Bundle options) {
+ if (checkCallingPermission(START_TASKS_FROM_RECENTS) != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: startActivityFromRecents called without " +
+ START_TASKS_FROM_RECENTS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
+ throw new ActivityNotFoundException("Task " + taskId + " not found.");
+ }
+ return startActivityInPackage(task.mCallingUid, task.mCallingPackage,
+ task.intent, null, null, null, 0, 0, options, task.userId,
+ null);
+ }
+
final int startActivityInPackage(int uid, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, Bundle options, int userId,
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 767a970ebe0ba..69949a418cce1 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -68,6 +68,8 @@ final class TaskRecord {
private static final String ATTR_TASK_AFFILIATION = "task_affiliation";
private static final String ATTR_PREV_AFFILIATION = "prev_affiliation";
private static final String ATTR_NEXT_AFFILIATION = "next_affiliation";
+ private static final String ATTR_CALLING_UID = "calling_uid";
+ private static final String ATTR_CALLING_PACKAGE = "calling_package";
private static final String LAST_ACTIVITY_ICON_SUFFIX = "_last_activity_icon_";
private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
@@ -144,6 +146,10 @@ final class TaskRecord {
TaskRecord mNextAffiliate; // next task in affiliated chain.
int mNextAffiliateTaskId = -1; // next id for persistence.
+ // For relaunching the task from recents as though it was launched by the original launcher.
+ int mCallingUid;
+ String mCallingPackage;
+
final ActivityManagerService mService;
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
@@ -167,7 +173,7 @@ final class TaskRecord {
String _lastDescription, ArrayList activities, long _firstActiveTime,
long _lastActiveTime, long lastTimeMoved, boolean neverRelinquishIdentity,
ActivityManager.TaskDescription _lastTaskDescription, int taskAffiliation,
- int prevTaskId, int nextTaskId) {
+ int prevTaskId, int nextTaskId, int callingUid, String callingPackage) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
@@ -196,6 +202,8 @@ final class TaskRecord {
mAffiliatedTaskId = taskAffiliation;
mPrevAffiliateTaskId = prevTaskId;
mNextAffiliateTaskId = nextTaskId;
+ mCallingUid = callingUid;
+ mCallingPackage = callingPackage;
}
void touchActiveTime() {
@@ -458,6 +466,8 @@ final class TaskRecord {
if (mActivities.isEmpty()) {
taskType = r.mActivityType;
isPersistable = r.isPersistable();
+ mCallingUid = r.launchedFromUid;
+ mCallingPackage = r.launchedFromPackage;
// Clamp to [1, 100].
maxRecents = Math.min(Math.max(r.info.maxRecents, 1), 100);
} else {
@@ -765,6 +775,8 @@ final class TaskRecord {
out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
+ out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
+ out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
if (affinityIntent != null) {
out.startTag(null, TAG_AFFINITYINTENT);
@@ -816,6 +828,8 @@ final class TaskRecord {
int taskAffiliation = -1;
int prevTaskId = -1;
int nextTaskId = -1;
+ int callingUid = -1;
+ String callingPackage = "";
for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
final String attrName = in.getAttributeName(attrNdx);
@@ -858,6 +872,10 @@ final class TaskRecord {
prevTaskId = Integer.valueOf(attrValue);
} else if (ATTR_NEXT_AFFILIATION.equals(attrName)) {
nextTaskId = Integer.valueOf(attrValue);
+ } else if (ATTR_CALLING_UID.equals(attrName)) {
+ callingUid = Integer.valueOf(attrValue);
+ } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
+ callingPackage = attrValue;
} else {
Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
}
@@ -898,7 +916,8 @@ final class TaskRecord {
affinityIntent, affinity, realActivity, origActivity, rootHasReset,
autoRemoveRecents, askedCompatMode, taskType, userId, lastDescription, activities,
firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
- taskDescription, taskAffiliation, prevTaskId, nextTaskId);
+ taskDescription, taskAffiliation, prevTaskId, nextTaskId, callingUid,
+ callingPackage);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
activities.get(activityNdx).task = task;