Merge "Formalize recents component in the system"

This commit is contained in:
Winson Chung
2017-10-26 21:04:06 +00:00
committed by Android (Google) Code Review
13 changed files with 397 additions and 93 deletions

View File

@@ -102,7 +102,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
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;

View File

@@ -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>

View File

@@ -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" />

View File

@@ -302,11 +302,6 @@ public class RecentsTransitionHelper {
*/
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();

View File

@@ -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.
@@ -2773,7 +2774,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);
@@ -2819,6 +2820,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;
}
@@ -3154,6 +3163,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();
@@ -3190,7 +3200,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);
}
@@ -3199,7 +3210,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);
}
@@ -4877,12 +4889,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) {
@@ -8334,9 +8343,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) {
@@ -8410,6 +8416,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
@@ -9799,6 +9814,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) {
@@ -9846,8 +9866,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) {
@@ -10041,7 +10060,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) {
@@ -10060,7 +10080,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) {
@@ -10079,7 +10100,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;
@@ -10132,12 +10153,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()) {
@@ -10157,7 +10179,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 {
@@ -10170,7 +10193,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 {
@@ -10199,7 +10223,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 {
@@ -10376,7 +10400,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 {
@@ -10412,7 +10436,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 {
@@ -10463,7 +10487,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 {
@@ -10502,12 +10526,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 */,
@@ -10530,14 +10558,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) {
@@ -10567,7 +10595,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:"
@@ -10586,13 +10615,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) {
@@ -10621,8 +10651,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) {
@@ -10637,8 +10666,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) {
@@ -10697,7 +10725,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@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 @@ 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) {
@@ -11871,8 +11899,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) {
@@ -18135,8 +18162,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// =========================================================
@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 @@ 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) {
@@ -20120,7 +20146,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 {

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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<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 @@ 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)");
}
}

View File

@@ -2020,7 +2020,7 @@ class UserController implements Handler.Callback {
void loadUserRecents(int userId) {
synchronized (mService) {
mService.mRecentTasks.loadUserRecentsLocked(userId);
mService.getRecentTasks().loadUserRecentsLocked(userId);
}
}

View File

@@ -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;
}

View File

@@ -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<TaskRecord> mTasks = new ArrayList<>();
private static ArrayList<TaskRecord> 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<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) {
@@ -108,12 +131,12 @@ public class RecentTasksTest extends ActivityTestsBase {
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 @@ 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<TaskRecord> added = new ArrayList<>();
ArrayList<TaskRecord> 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<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);
}
}
}