From dc00cbe2c15eef5141656311a6d05c753d7c6db6 Mon Sep 17 00:00:00 2001 From: Craig Mautner Date: Sun, 20 Jul 2014 17:48:47 -0700 Subject: [PATCH] Create API for launching from recents Relaunch apps from recents using the same uid and package as was used in the original launch. Fixes bug 16358208. Change-Id: I340800bfee79926b197929360d69f1d570bbf1eb --- .../android/app/ActivityManagerNative.java | 29 +++++++++++++++++++ core/java/android/app/IActivityManager.java | 2 ++ core/res/AndroidManifest.xml | 8 +++++ core/res/res/values/strings.xml | 6 ++++ packages/SystemUI/AndroidManifest.xml | 1 + .../recents/misc/SystemServicesProxy.java | 9 ++++++ .../systemui/recents/views/RecentsView.java | 16 +++------- .../server/am/ActivityManagerService.java | 18 ++++++++++++ .../com/android/server/am/TaskRecord.java | 23 +++++++++++++-- 9 files changed, 98 insertions(+), 14 deletions(-) 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;