From 3f0e59ac1e308ca8d70e04952da19fd2682c88f1 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 25 Oct 2017 10:19:05 -0700 Subject: Formalize recents component in the system - Add resource for recents component which can be overridden in an overlay - Allow the static recents component access to APIs which would otherwise require privileged permissions - Remove some unused TV recents code Bug: 67864419 Test: com.android.server.am.RecentTasksTest Test: #testNotRecentsComponent_denyApiAccess Test: #testRecentsComponent_allowApiAccessWithoutPermissions Change-Id: Ia4939b7d443a6058ab4bb41d8c194eb4611cbf80 --- core/java/android/app/WindowConfiguration.java | 2 +- core/res/res/values/config.xml | 4 + core/res/res/values/symbols.xml | 1 + .../recents/views/RecentsTransitionHelper.java | 5 - .../android/server/am/ActivityManagerService.java | 111 +++++---- .../server/am/ActivityManagerShellCommand.java | 2 +- .../java/com/android/server/am/ActivityRecord.java | 17 +- .../android/server/am/ActivityStackSupervisor.java | 13 +- .../java/com/android/server/am/AppTaskImpl.java | 2 +- .../java/com/android/server/am/RecentTasks.java | 75 +++++-- .../java/com/android/server/am/UserController.java | 2 +- .../com/android/server/am/ActivityTestsBase.java | 6 +- .../src/com/android/server/am/RecentTasksTest.java | 250 ++++++++++++++++++++- 13 files changed, 397 insertions(+), 93 deletions(-) diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index 251863cae205..de27b4fd97f0 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -102,7 +102,7 @@ public class WindowConfiguration implements Parcelable, Comparable true + + com.android.systemui/.recents.RecentsActivity + -1 diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 896de53cbc4e..3dff9d789a17 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -311,6 +311,7 @@ + diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java index 25c2fc97eda4..7442904ec1c0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java @@ -302,11 +302,6 @@ public class RecentsTransitionHelper { */ private List composeAnimationSpecs(final Task task, final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) { - if (activityType == ACTIVITY_TYPE_RECENTS || activityType == ACTIVITY_TYPE_HOME - || windowingMode == WINDOWING_MODE_PINNED) { - return null; - } - // Calculate the offscreen task rect (for tasks that are not backed by views) TaskView taskView = stackView.getChildViewForTask(task); TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8839af47fd93..0f68e3c23b59 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -23,6 +23,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.READ_FRAME_BUFFER; +import static android.Manifest.permission.REMOVE_TASKS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; @@ -672,7 +673,7 @@ public class ActivityManagerService extends IActivityManager.Stub /** * List of intents that were used to start the most recent tasks. */ - final RecentTasks mRecentTasks; + private final RecentTasks mRecentTasks; /** * For addAppTask: cached of the last activity component that was added. @@ -2771,7 +2772,7 @@ public class ActivityManagerService extends IActivityManager.Stub mTaskChangeNotificationController = new TaskChangeNotificationController(this, mStackSupervisor, mHandler); mActivityStarter = new ActivityStarter(this); - mRecentTasks = new RecentTasks(this, mStackSupervisor); + mRecentTasks = createRecentTasks(); mStackSupervisor.setRecentTasks(mRecentTasks); mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler); @@ -2817,6 +2818,14 @@ public class ActivityManagerService extends IActivityManager.Stub return new ActivityStackSupervisor(this, mHandler.getLooper()); } + protected RecentTasks createRecentTasks() { + return new RecentTasks(this, mStackSupervisor); + } + + RecentTasks getRecentTasks() { + return mRecentTasks; + } + public void setSystemServiceManager(SystemServiceManager mgr) { mSystemServiceManager = mgr; } @@ -3152,6 +3161,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { final ActivityStack stack = mStackSupervisor.getStack(stackId); if (stack == null) { + Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId); return; } final ActivityRecord r = stack.topRunningActivityLocked(); @@ -3188,7 +3198,8 @@ public class ActivityManagerService extends IActivityManager.Stub /** Sets the task stack listener that gets callbacks when a task stack changes. */ @Override public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "registerTaskStackListener()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, + "registerTaskStackListener()"); mTaskChangeNotificationController.registerTaskStackListener(listener); } @@ -3197,7 +3208,8 @@ public class ActivityManagerService extends IActivityManager.Stub */ @Override public void unregisterTaskStackListener(ITaskStackListener listener) throws RemoteException { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "unregisterTaskStackListener()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, + "unregisterTaskStackListener()"); mTaskChangeNotificationController.unregisterTaskStackListener(listener); } @@ -4875,12 +4887,9 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public final int startActivityFromRecents(int taskId, Bundle bOptions) { - 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); - } + enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS, + "startActivityFromRecents()"); + final long origId = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -8332,9 +8341,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - /** - * This can be called with or without the global lock held. - */ int checkComponentPermission(String permission, int pid, int uid, int owningUid, boolean exported) { if (pid == MY_PID) { @@ -8408,6 +8414,15 @@ public class ActivityManagerService extends IActivityManager.Stub throw new SecurityException(msg); } + /** + * This can be called with or without the global lock held. + */ + void enforceCallerIsRecentsOrHasPermission(String permission, String func) { + if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) { + enforceCallingPermission(permission, func); + } + } + /** * Determine if UID is holding permissions required to access {@link Uri} in * the given {@link ProviderInfo}. Final permission checking is always done @@ -9797,6 +9812,11 @@ public class ActivityManagerService extends IActivityManager.Stub } private boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) { + if (mRecentTasks.isCallerRecents(callingUid)) { + // Always allow the recents component to get tasks + return true; + } + boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; if (!allowed) { @@ -9844,8 +9864,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public ActivityManager.TaskDescription getTaskDescription(int id) { synchronized (this) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "getTaskDescription()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getTaskDescription()"); final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr != null) { @@ -10039,7 +10058,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void cancelTaskWindowTransition(int taskId) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "cancelTaskWindowTransition()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, + "cancelTaskWindowTransition()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -10058,7 +10078,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void cancelTaskThumbnailTransition(int taskId) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "cancelTaskThumbnailTransition()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, + "cancelTaskThumbnailTransition()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -10077,7 +10098,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) { - enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()"); + enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()"); final long ident = Binder.clearCallingIdentity(); try { final TaskRecord task; @@ -10130,12 +10151,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void removeStack(int stackId) { - enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()"); synchronized (this) { final long ident = Binder.clearCallingIdentity(); try { final ActivityStack stack = mStackSupervisor.getStack(stackId); if (stack == null) { + Slog.w(TAG, "removeStack: No stack with id=" + stackId); return; } if (!stack.isActivityTypeStandardOrUndefined()) { @@ -10155,7 +10177,8 @@ public class ActivityManagerService extends IActivityManager.Stub */ @Override public void removeStacksInWindowingModes(int[] windowingModes) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksInWindowingModes()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, + "removeStacksInWindowingModes()"); synchronized (this) { final long ident = Binder.clearCallingIdentity(); try { @@ -10168,7 +10191,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void removeStacksWithActivityTypes(int[] activityTypes) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksWithActivityTypes()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, + "removeStacksWithActivityTypes()"); synchronized (this) { final long ident = Binder.clearCallingIdentity(); try { @@ -10197,7 +10221,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public boolean removeTask(int taskId) { - enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, "removeTask()"); + enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()"); synchronized (this) { final long ident = Binder.clearCallingIdentity(); try { @@ -10374,7 +10398,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()"); synchronized (this) { final long ident = Binder.clearCallingIdentity(); try { @@ -10410,7 +10434,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void moveTaskToStack(int taskId, int stackId, boolean toTop) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()"); synchronized (this) { long ident = Binder.clearCallingIdentity(); try { @@ -10461,7 +10485,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate, Rect initialBounds) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()"); synchronized (this) { long ident = Binder.clearCallingIdentity(); try { @@ -10500,12 +10524,16 @@ public class ActivityManagerService extends IActivityManager.Stub */ @Override public void dismissSplitScreenMode(boolean toTop) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (this) { final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack(); + if (stack == null) { + Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found."); + return; + } if (toTop) { mStackSupervisor.resizeStackLocked(stack, null /* destBounds */, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, @@ -10528,14 +10556,14 @@ public class ActivityManagerService extends IActivityManager.Stub */ @Override public void dismissPip(boolean animate, int animationDuration) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (this) { final PinnedActivityStack stack = mStackSupervisor.getDefaultDisplay().getPinnedStack(); - if (stack == null) { + Slog.w(TAG, "dismissPip: pinned stack not found."); return; } if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { @@ -10565,7 +10593,8 @@ public class ActivityManagerService extends IActivityManager.Stub */ @Override public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTopActivityToPinnedStack()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, + "moveTopActivityToPinnedStack()"); synchronized (this) { if (!mSupportsPictureInPicture) { throw new IllegalStateException("moveTopActivityToPinnedStack:" @@ -10584,13 +10613,14 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode, boolean preserveWindows, boolean animate, int animationDuration) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { if (animate) { final PinnedActivityStack stack = mStackSupervisor.getStack(stackId); if (stack == null) { + Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); return; } if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { @@ -10619,8 +10649,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds, Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "resizeDockedStack()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -10635,8 +10664,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "resizePinnedStack()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -10695,7 +10723,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public List getAllStackInfos() { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -10708,7 +10736,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public StackInfo getStackInfo(int windowingMode, int activityType) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -11869,8 +11897,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void appNotRespondingViaProvider(IBinder connection) { - enforceCallingPermission( - android.Manifest.permission.REMOVE_TASKS, "appNotRespondingViaProvider()"); + enforceCallingPermission(REMOVE_TASKS, "appNotRespondingViaProvider()"); final ContentProviderConnection conn = (ContentProviderConnection) connection; if (conn == null) { @@ -18133,8 +18160,7 @@ public class ActivityManagerService extends IActivityManager.Stub // ========================================================= @Override - public List getServices(int maxNum, - int flags) { + public List getServices(int maxNum, int flags) { enforceNotIsolatedCaller("getServices"); final int callingUid = Binder.getCallingUid(); @@ -20078,7 +20104,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public StackInfo getFocusedStackInfo() throws RemoteException { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -20118,7 +20144,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override // TODO: API should just be about changing windowing modes... public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTasksToFullscreenStack()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, + "moveTasksToFullscreenStack()"); synchronized (this) { final long origId = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 4cf2794c3584..f9422656c7db 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2305,7 +2305,7 @@ final class ActivityManagerShellCommand extends ShellCommand { int runWrite(PrintWriter pw) { mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, "registerUidObserver()"); - mInternal.mRecentTasks.flush(); + mInternal.getRecentTasks().flush(); pw.println("All tasks persisted."); return 0; } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index b47f81948edb..61c226da9a48 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -204,7 +204,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY; private static final boolean SHOW_ACTIVITY_START_TIME = true; - private static final String RECENTS_PACKAGE_NAME = "com.android.systemui.recents"; private static final String ATTR_ID = "id"; private static final String TAG_INTENT = "intent"; @@ -1058,7 +1057,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // We only allow home activities to be resizeable if they explicitly requested it. info.resizeMode = RESIZE_MODE_UNRESIZEABLE; } - } else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) { + } else if (service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) { activityType = ACTIVITY_TYPE_RECENTS; } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT && canLaunchAssistActivity(launchedFromPackage)) { @@ -1562,17 +1561,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return false; } - boolean isVisible = !behindFullscreenActivity || mLaunchTaskBehind; - - if (service.mSupportsLeanbackOnly && isVisible && isActivityTypeRecents()) { - // On devices that support leanback only (Android TV), Recents activity can only be - // visible if the home stack is the focused stack or we are in split-screen mode. - final ActivityDisplay display = getDisplay(); - boolean hasSplitScreenStack = display != null && display.hasSplitScreenPrimaryStack(); - isVisible = hasSplitScreenStack || mStackSupervisor.isFocusedStack(getStack()); - } - - return isVisible; + return !behindFullscreenActivity || mLaunchTaskBehind; } void makeVisibleIfNeeded(ActivityRecord starting) { @@ -2074,7 +2063,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo final File iconFile = new File(TaskPersister.getUserImagesDir(task.userId), iconFilename); final String iconFilePath = iconFile.getAbsolutePath(); - service.mRecentTasks.saveImage(icon, iconFilePath); + service.getRecentTasks().saveImage(icon, iconFilePath); _taskDescription.setIconFilename(iconFilePath); } taskDescription = _taskDescription; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 0db65913889a..0c8d0391bc72 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -285,7 +285,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D RecentTasks mRecentTasks; /** Helper class to abstract out logic for fetching the set of currently running tasks */ - private RunningTasks mRunningTasks = new RunningTasks(); + private RunningTasks mRunningTasks; final ActivityStackSupervisorHandler mHandler; @@ -572,6 +572,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D public ActivityStackSupervisor(ActivityManagerService service, Looper looper) { mService = service; mHandler = new ActivityStackSupervisorHandler(looper); + mRunningTasks = createRunningTasks(); mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext); mKeyguardController = new KeyguardController(service, this); @@ -584,6 +585,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mRecentTasks.registerCallback(this); } + @VisibleForTesting + RunningTasks createRunningTasks() { + return new RunningTasks(); + } + /** * At the time when the constructor runs, the power manager has not yet been * initialized. So we initialize our wakelocks afterwards. @@ -1559,7 +1565,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } if (options != null) { - if (options.getLaunchTaskId() != INVALID_STACK_ID) { + // If a launch task id is specified, then ensure that the caller is the recents + // component or has the START_TASKS_FROM_RECENTS permission + if (options.getLaunchTaskId() != INVALID_TASK_ID + && !mRecentTasks.isCallerRecents(callingUid)) { final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS, callingPid, callingUid); if (startInTaskPerm == PERMISSION_DENIED) { diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java index 66e975fc849c..17626ea166af 100644 --- a/services/core/java/com/android/server/am/AppTaskImpl.java +++ b/services/core/java/com/android/server/am/AppTaskImpl.java @@ -82,7 +82,7 @@ class AppTaskImpl extends IAppTask.Stub { if (tr == null) { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); } - return mService.mRecentTasks.createRecentTaskInfo(tr); + return mService.getRecentTasks().createRecentTaskInfo(tr); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index ea6e9194e976..0b9e0a23a4e8 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -37,8 +37,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; -import com.google.android.collect.Sets; - import android.app.ActivityManager; import android.app.AppGlobals; import android.content.ComponentName; @@ -58,9 +56,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; +import android.text.TextUtils; import android.util.ArraySet; -import android.util.MutableBoolean; -import android.util.MutableInt; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -68,6 +65,8 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.VisibleForTesting; import com.android.server.am.TaskRecord.TaskActivitiesReport; +import com.google.android.collect.Sets; + import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; @@ -124,6 +123,13 @@ class RecentTasks { private final ActivityManagerService mService; private final UserController mUserController; + /** + * Keeps track of the static recents package/component which is granted additional permissions + * to call recents-related APIs. + */ + private int mRecentsUid = -1; + private ComponentName mRecentsComponent = null; + /** * Mapping of user id -> whether recent tasks have been loaded for that user. */ @@ -153,8 +159,7 @@ class RecentTasks { private final HashMap mTmpAvailActCache = new HashMap<>(); private final HashMap mTmpAvailAppCache = new HashMap<>(); private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray(); - private final TaskRecord.TaskActivitiesReport mTmpReport = - new TaskRecord.TaskActivitiesReport(); + private final TaskActivitiesReport mTmpReport = new TaskActivitiesReport(); @VisibleForTesting RecentTasks(ActivityManagerService service, TaskPersister taskPersister, @@ -174,7 +179,7 @@ class RecentTasks { mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this); mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic(); mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents); - loadParametersFromResources(service.mContext.getResources()); + loadParametersFromResources(res); } @VisibleForTesting @@ -218,6 +223,47 @@ class RecentTasks { : -1; } + /** + * Loads the static recents component. This is called after the system is ready, but before + * any dependent services (like SystemUI) is started. + */ + void loadRecentsComponent(Resources res) { + final String rawRecentsComponent = res.getString( + com.android.internal.R.string.config_recentsComponentName); + if (TextUtils.isEmpty(rawRecentsComponent)) { + return; + } + + final ComponentName cn = ComponentName.unflattenFromString(rawRecentsComponent); + if (cn != null) { + try { + final ApplicationInfo appInfo = AppGlobals.getPackageManager() + .getApplicationInfo(cn.getPackageName(), 0, mService.mContext.getUserId()); + if (appInfo != null) { + mRecentsUid = appInfo.uid; + mRecentsComponent = cn; + } + } catch (RemoteException e) { + Slog.w(TAG, "Could not load application info for recents component: " + cn); + } + } + } + + /** + * @return whether the current caller has the same uid as the recents component. + */ + boolean isCallerRecents(int callingUid) { + return UserHandle.isSameApp(callingUid, mRecentsUid); + } + + /** + * @return whether the given component is the recents component and shares the same uid as the + * recents component. + */ + boolean isRecentsComponent(ComponentName cn, int uid) { + return cn.equals(mRecentsComponent) && UserHandle.isSameApp(uid, mRecentsUid); + } + void registerCallback(Callbacks callback) { mCallbacks.add(callback); } @@ -340,6 +386,7 @@ class RecentTasks { } void onSystemReadyLocked() { + loadRecentsComponent(mService.mContext.getResources()); mTasks.clear(); mTaskPersister.startPersisting(); } @@ -1328,12 +1375,14 @@ class RecentTasks { void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) { pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)"); + pw.println("mRecentsUid=" + mRecentsUid); + pw.println("mRecentsComponent=" + mRecentsComponent); if (mTasks.isEmpty()) { return; } - final MutableBoolean printedAnything = new MutableBoolean(false); - final MutableBoolean printedHeader = new MutableBoolean(false); + boolean printedAnything = false; + boolean printedHeader = false; final int size = mTasks.size(); for (int i = 0; i < size; i++) { final TaskRecord tr = mTasks.get(i); @@ -1342,10 +1391,10 @@ class RecentTasks { continue; } - if (!printedHeader.value) { + if (!printedHeader) { pw.println(" Recent tasks:"); - printedHeader.value = true; - printedAnything.value = true; + printedHeader = true; + printedAnything = true; } pw.print(" * Recent #"); pw.print(i); pw.print(": "); pw.println(tr); @@ -1354,7 +1403,7 @@ class RecentTasks { } } - if (!printedAnything.value) { + if (!printedAnything) { pw.println(" (nothing)"); } } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index a0c5cfaa7908..1b42818a14ad 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -2020,7 +2020,7 @@ class UserController implements Handler.Callback { void loadUserRecents(int userId) { synchronized (mService) { - mService.mRecentTasks.loadUserRecentsLocked(userId); + mService.getRecentTasks().loadUserRecentsLocked(userId); } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index 3d5d87c1359a..198cc6ded558 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -78,7 +78,11 @@ public class ActivityTestsBase { } protected ActivityManagerService createActivityManagerService() { - final ActivityManagerService service = spy(new TestActivityManagerService(mContext)); + return setupActivityManagerService(new TestActivityManagerService(mContext)); + } + + protected ActivityManagerService setupActivityManagerService(ActivityManagerService service) { + service = spy(service); service.mWindowManager = prepareMockWindowManager(); return service; } diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java index 939e989c5fe8..3c9b54215f80 100644 --- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java +++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java @@ -16,44 +16,55 @@ package com.android.server.am; +import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; +import static java.lang.Integer.MAX_VALUE; + +import android.app.ActivityManager.RecentTaskInfo; +import android.app.ActivityManager.RunningTaskInfo; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; -import android.os.Debug; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.Looper; +import android.os.RemoteException; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; import android.util.MutableLong; +import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.server.am.RecentTasks.Callbacks; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java @@ -67,13 +78,17 @@ public class RecentTasksTest extends ActivityTestsBase { private static final int TEST_QUIET_USER_ID = 20; private static final UserInfo DEFAULT_USER_INFO = new UserInfo(); private static final UserInfo QUIET_USER_INFO = new UserInfo(); + private static final ComponentName MY_COMPONENT = new ComponentName( + RecentTasksTest.class.getPackage().getName(), RecentTasksTest.class.getName()); private static int LAST_TASK_ID = 1; + private static int INVALID_STACK_ID = 999; private Context mContext = InstrumentationRegistry.getContext(); private ActivityManagerService mService; private ActivityStack mStack; private TestTaskPersister mTaskPersister; private RecentTasks mRecentTasks; + private RunningTasks mRunningTasks; private static ArrayList mTasks = new ArrayList<>(); private static ArrayList mSameDocumentTasks = new ArrayList<>(); @@ -90,6 +105,14 @@ public class RecentTasksTest extends ActivityTestsBase { return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID }; } + @Override + Set getProfileIds(int userId) { + Set profileIds = new HashSet<>(); + profileIds.add(TEST_USER_0_ID); + profileIds.add(TEST_QUIET_USER_ID); + return profileIds; + } + @Override UserInfo getUserInfo(int userId) { switch (userId) { @@ -108,12 +131,12 @@ public class RecentTasksTest extends ActivityTestsBase { public void setUp() throws Exception { super.setUp(); - mService = createActivityManagerService(); - mStack = mService.mStackSupervisor.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mTaskPersister = new TestTaskPersister(mContext.getFilesDir()); - mRecentTasks = new RecentTasks(mService, mTaskPersister, new TestUserController(mService)); + mService = setupActivityManagerService(new MyTestActivityManagerService(mContext)); + mRecentTasks = mService.getRecentTasks(); mRecentTasks.loadParametersFromResources(mContext.getResources()); + mStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mCallbacksRecorder = new CallbacksRecorder(); mRecentTasks.registerCallback(mCallbacksRecorder); QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE; @@ -322,6 +345,103 @@ public class RecentTasksTest extends ActivityTestsBase { assertTrimmed(mTasks.get(0), mTasks.get(1)); } + @Test + public void testNotRecentsComponent_denyApiAccess() throws Exception { + doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(), + anyInt(), anyInt()); + + // Expect the following methods to fail due to recents component not being set + ((TestRecentTasks) mRecentTasks).setIsCallerRecentsOverride( + TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION); + testRecentTasksApis(false /* expectNoSecurityException */); + // Don't throw for the following tests + ((TestRecentTasks) mRecentTasks).setIsCallerRecentsOverride(TestRecentTasks.DENY); + testGetTasksApis(false /* expectNoSecurityException */); + } + + @Test + public void testRecentsComponent_allowApiAccessWithoutPermissions() throws Exception { + doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(), + anyInt(), anyInt()); + + // Set the recents component and ensure that the following calls do not fail + ((TestRecentTasks) mRecentTasks).setIsCallerRecentsOverride(TestRecentTasks.GRANT); + testRecentTasksApis(true /* expectNoSecurityException */); + testGetTasksApis(true /* expectNoSecurityException */); + } + + private void testRecentTasksApis(boolean expectCallable) { + assertSecurityException(expectCallable, () -> mService.removeStack(INVALID_STACK_ID)); + assertSecurityException(expectCallable, + () -> mService.removeStacksInWindowingModes(new int[] {WINDOWING_MODE_UNDEFINED})); + assertSecurityException(expectCallable, + () -> mService.removeStacksWithActivityTypes(new int[] {ACTIVITY_TYPE_UNDEFINED})); + assertSecurityException(expectCallable, () -> mService.removeTask(0)); + assertSecurityException(expectCallable, + () -> mService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true)); + assertSecurityException(expectCallable, + () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true)); + assertSecurityException(expectCallable, + () -> mService.moveTaskToDockedStack(0, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, true, + true, new Rect())); + assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true)); + assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0)); + assertSecurityException(expectCallable, + () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect())); + assertSecurityException(expectCallable, + () -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0)); + assertSecurityException(expectCallable, + () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(), + new Rect())); + assertSecurityException(expectCallable, + () -> mService.resizePinnedStack(new Rect(), new Rect())); + assertSecurityException(expectCallable, () -> mService.getAllStackInfos()); + assertSecurityException(expectCallable, + () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED)); + assertSecurityException(expectCallable, () -> { + try { + mService.getFocusedStackInfo(); + } catch (RemoteException e) { + // Ignore + } + }); + assertSecurityException(expectCallable, + () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true)); + assertSecurityException(expectCallable, + () -> mService.startActivityFromRecents(0, new Bundle())); + assertSecurityException(expectCallable, + () -> mService.getTaskSnapshot(0, true)); + assertSecurityException(expectCallable, () -> { + try { + mService.registerTaskStackListener(null); + } catch (RemoteException e) { + // Ignore + } + }); + assertSecurityException(expectCallable, () -> { + try { + mService.unregisterTaskStackListener(null); + } catch (RemoteException e) { + // Ignore + } + }); + assertSecurityException(expectCallable, () -> mService.getTaskDescription(0)); + assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0)); + assertSecurityException(expectCallable, () -> mService.cancelTaskThumbnailTransition(0)); + } + + private void testGetTasksApis(boolean expectCallable) { + mService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID); + mService.getTasks(MAX_VALUE); + if (expectCallable) { + assertTrue(((TestRecentTasks) mRecentTasks).mLastAllowed); + assertTrue(((TestRunningTasks) mRunningTasks).mLastAllowed); + } else { + assertFalse(((TestRecentTasks) mRecentTasks).mLastAllowed); + assertFalse(((TestRunningTasks) mRunningTasks).mLastAllowed); + } + } + private ComponentName createComponent(String className) { return new ComponentName(mContext.getPackageName(), className); } @@ -367,6 +487,55 @@ public class RecentTasksTest extends ActivityTestsBase { } } + private void assertSecurityException(boolean expectCallable, Runnable runnable) { + boolean noSecurityException = true; + try { + runnable.run(); + } catch (SecurityException se) { + noSecurityException = false; + } catch (Exception e) { + // We only care about SecurityExceptions, fall through here + e.printStackTrace(); + } + if (noSecurityException != expectCallable) { + fail("Expected callable: " + expectCallable + " but got no security exception: " + + noSecurityException); + } + } + + private class MyTestActivityManagerService extends TestActivityManagerService { + MyTestActivityManagerService(Context context) { + super(context); + } + + @Override + protected ActivityStackSupervisor createStackSupervisor() { + return new MyTestActivityStackSupervisor(this, mHandlerThread.getLooper()); + } + + @Override + protected RecentTasks createRecentTasks() { + return new TestRecentTasks(this, mTaskPersister, new TestUserController(this)); + } + + @Override + public boolean isUserRunning(int userId, int flags) { + return true; + } + } + + private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor { + public MyTestActivityStackSupervisor(ActivityManagerService service, Looper looper) { + super(service, looper); + } + + @Override + RunningTasks createRunningTasks() { + mRunningTasks = new TestRunningTasks(); + return mRunningTasks; + } + } + private static class CallbacksRecorder implements Callbacks { ArrayList added = new ArrayList<>(); ArrayList trimmed = new ArrayList<>(); @@ -417,4 +586,61 @@ public class RecentTasksTest extends ActivityTestsBase { return super.restoreTasksForUserLocked(userId, preaddedTasks); } } + + private static class TestRecentTasks extends RecentTasks { + static final int GRANT = 0; + static final int DENY = 1; + static final int DENY_THROW_SECURITY_EXCEPTION = 2; + + private boolean mOverrideIsCallerRecents; + private int mIsCallerRecentsPolicy; + boolean mLastAllowed; + + TestRecentTasks(ActivityManagerService service, TaskPersister taskPersister, + UserController userController) { + super(service, taskPersister, userController); + } + + @Override + boolean isCallerRecents(int callingUid) { + if (mOverrideIsCallerRecents) { + switch (mIsCallerRecentsPolicy) { + case GRANT: + return true; + case DENY: + return false; + case DENY_THROW_SECURITY_EXCEPTION: + throw new SecurityException(); + } + } + return super.isCallerRecents(callingUid); + } + + void setIsCallerRecentsOverride(int policy) { + mOverrideIsCallerRecents = true; + mIsCallerRecentsPolicy = policy; + } + + @Override + ParceledListSlice getRecentTasks(int maxNum, int flags, + boolean getTasksAllowed, + boolean getDetailedTasks, int userId, int callingUid) { + mLastAllowed = getTasksAllowed; + return super.getRecentTasks(maxNum, flags, getTasksAllowed, getDetailedTasks, userId, + callingUid); + } + } + + private static class TestRunningTasks extends RunningTasks { + boolean mLastAllowed; + + @Override + void getTasks(int maxNum, List list, int ignoreActivityType, + int ignoreWindowingMode, SparseArray activityDisplays, + int callingUid, boolean allowed) { + mLastAllowed = allowed; + super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays, + callingUid, allowed); + } + } } \ No newline at end of file -- cgit v1.2.3-59-g8ed1b