Merge "Formalize recents component in the system"
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 251863c..de27b4f 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -102,7 +102,7 @@
public static final int ACTIVITY_TYPE_STANDARD = 1;
/** Home/Launcher activity type. */
public static final int ACTIVITY_TYPE_HOME = 2;
- /** Recents/Overview activity type. */
+ /** Recents/Overview activity type. There is only one activity with this type in the system. */
public static final int ACTIVITY_TYPE_RECENTS = 3;
/** Assistant activity type. */
public static final int ACTIVITY_TYPE_ASSISTANT = 4;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 571aad9..e5f3aaf 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2356,6 +2356,10 @@
property. If this is false, then the following recents config flags are ignored. -->
<bool name="config_hasRecents">true</bool>
+ <!-- Component name for the activity that will be presenting the Recents UI, which will receive
+ special permissions for API related to fetching and presenting recent tasks. -->
+ <string name="config_recentsComponentName" translatable="false">com.android.systemui/.recents.RecentsActivity</string>
+
<!-- The minimum number of visible recent tasks to be presented to the user through the
SystemUI. Can be -1 if there is no minimum limit. -->
<integer name="config_minNumVisibleRecentTasks_grid">-1</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 896de53..3dff9d7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -311,6 +311,7 @@
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
<java-symbol type="bool" name="config_hasRecents" />
+ <java-symbol type="string" name="config_recentsComponentName" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
<java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" />
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 25c2fc9..7442904 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 @@
*/
private List<AppTransitionAnimationSpec> 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 d3a6719..93c0a39 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.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 @@
/**
* 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.
@@ -2773,7 +2774,7 @@
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);
@@ -2819,6 +2820,14 @@
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;
}
@@ -3154,6 +3163,7 @@
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();
@@ -3190,7 +3200,8 @@
/** 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);
}
@@ -3199,7 +3210,8 @@
*/
@Override
public void unregisterTaskStackListener(ITaskStackListener listener) throws RemoteException {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "unregisterTaskStackListener()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ "unregisterTaskStackListener()");
mTaskChangeNotificationController.unregisterTaskStackListener(listener);
}
@@ -4877,12 +4889,9 @@
@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) {
@@ -8334,9 +8343,6 @@
}
}
- /**
- * 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) {
@@ -8411,6 +8417,15 @@
}
/**
+ * 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
* in {@link ContentProvider}.
@@ -9799,6 +9814,11 @@
}
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) {
@@ -9846,8 +9866,7 @@
@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) {
@@ -10041,7 +10060,8 @@
@Override
public void cancelTaskWindowTransition(int taskId) {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "cancelTaskWindowTransition()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ "cancelTaskWindowTransition()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
@@ -10060,7 +10080,8 @@
@Override
public void cancelTaskThumbnailTransition(int taskId) {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "cancelTaskThumbnailTransition()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ "cancelTaskThumbnailTransition()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
@@ -10079,7 +10100,7 @@
@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;
@@ -10132,12 +10153,13 @@
@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()) {
@@ -10157,7 +10179,8 @@
*/
@Override
public void removeStacksInWindowingModes(int[] windowingModes) {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksInWindowingModes()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ "removeStacksInWindowingModes()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -10170,7 +10193,8 @@
@Override
public void removeStacksWithActivityTypes(int[] activityTypes) {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksWithActivityTypes()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ "removeStacksWithActivityTypes()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -10199,7 +10223,7 @@
@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 {
@@ -10376,7 +10400,7 @@
@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 {
@@ -10412,7 +10436,7 @@
@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 {
@@ -10463,7 +10487,7 @@
@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 {
@@ -10502,12 +10526,16 @@
*/
@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 */,
@@ -10530,14 +10558,14 @@
*/
@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) {
@@ -10567,7 +10595,8 @@
*/
@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:"
@@ -10586,13 +10615,14 @@
@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) {
@@ -10621,8 +10651,7 @@
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) {
@@ -10637,8 +10666,7 @@
@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) {
@@ -10697,7 +10725,7 @@
@Override
public List<StackInfo> getAllStackInfos() {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
@@ -10710,7 +10738,7 @@
@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) {
@@ -11871,8 +11899,7 @@
@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) {
@@ -18135,8 +18162,7 @@
// =========================================================
@Override
- public List<ActivityManager.RunningServiceInfo> getServices(int maxNum,
- int flags) {
+ public List<ActivityManager.RunningServiceInfo> getServices(int maxNum, int flags) {
enforceNotIsolatedCaller("getServices");
final int callingUid = Binder.getCallingUid();
@@ -20080,7 +20106,7 @@
@Override
public StackInfo getFocusedStackInfo() throws RemoteException {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
@@ -20120,7 +20146,8 @@
@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 4cf2794..f942265 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 @@
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 0e8fc2c..2f0b649 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 @@
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 @@
// 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 @@
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 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 63c3a54..6ec158e 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 @@
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 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 @@
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 @@
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 66e975f..17626ea 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 @@
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 ea6e919..0b9e0a2 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.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.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 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;
@@ -125,6 +124,13 @@
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.
*/
private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(
@@ -153,8 +159,7 @@
private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
private final HashMap<String, ApplicationInfo> 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 @@
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 @@
: -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 @@
}
void onSystemReadyLocked() {
+ loadRecentsComponent(mService.mContext.getResources());
mTasks.clear();
mTaskPersister.startPersisting();
}
@@ -1328,12 +1375,14 @@
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 @@
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 @@
}
}
- 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 a0c5cfa..1b42818 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 @@
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 3d5d87c..198cc6d 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 @@
}
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 939e989..3c9b542 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 @@
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<TaskRecord> mTasks = new ArrayList<>();
private static ArrayList<TaskRecord> mSameDocumentTasks = new ArrayList<>();
@@ -91,6 +106,14 @@
}
@Override
+ Set<Integer> getProfileIds(int userId) {
+ Set<Integer> profileIds = new HashSet<>();
+ profileIds.add(TEST_USER_0_ID);
+ profileIds.add(TEST_QUIET_USER_ID);
+ return profileIds;
+ }
+
+ @Override
UserInfo getUserInfo(int userId) {
switch (userId) {
case TEST_USER_0_ID:
@@ -108,12 +131,12 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
+ 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 */);
- mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
- mRecentTasks = new RecentTasks(mService, mTaskPersister, new TestUserController(mService));
- mRecentTasks.loadParametersFromResources(mContext.getResources());
mCallbacksRecorder = new CallbacksRecorder();
mRecentTasks.registerCallback(mCallbacksRecorder);
QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
@@ -322,6 +345,103 @@
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 @@
}
}
+ 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<TaskRecord> added = new ArrayList<>();
ArrayList<TaskRecord> trimmed = new ArrayList<>();
@@ -417,4 +586,61 @@
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<RecentTaskInfo> 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<RunningTaskInfo> list, int ignoreActivityType,
+ int ignoreWindowingMode, SparseArray<ActivityDisplay> activityDisplays,
+ int callingUid, boolean allowed) {
+ mLastAllowed = allowed;
+ super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays,
+ callingUid, allowed);
+ }
+ }
}
\ No newline at end of file