diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 034f5c8128f64..86493ebde535f 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -60,6 +60,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; +import static com.android.server.wm.ActivityRecord.State.FINISHING; import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; @@ -74,7 +75,12 @@ import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_COMPONENT; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_UID; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_BAL_PERMISSION; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_DEFAULT; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PENDING_INTENT; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW; import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; @@ -152,6 +158,7 @@ class ActivityStarter { private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS; private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING; + private static final int INVALID_LAUNCH_MODE = -1; /** @@ -1851,64 +1858,116 @@ class ActivityStarter { } } - // Log activity starts which violate one of the following rules of the - // activity security model (ASM): - // 1. Only the top activity on a task can start activities on that task - // 2. Only the top activity on the top task can create new (top) tasks - // We don't currently block, but these checks may later become blocks - // TODO(b/236234252): Shift to BackgroundActivityStartController once - // class is ready - if (mSourceRecord != null) { - int callerUid = mSourceRecord.getUid(); - ActivityRecord targetTopActivity = - targetTask != null ? targetTask.getTopNonFinishingActivity() : null; - boolean passesAsmChecks = newTask - ? mService.mVisibleActivityProcessTracker.hasResumedActivity(callerUid) - : targetTopActivity != null && targetTopActivity.getUid() == callerUid; - - if (!passesAsmChecks) { - Slog.i(TAG, "Launching r: " + r - + " from background: " + mSourceRecord - + ". New task: " + newTask); - boolean newOrEmptyTask = newTask || (targetTopActivity == null); - int action = newTask - ? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_NEW_TASK - : (mSourceRecord.getTask().equals(targetTask) - ? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_SAME_TASK - : FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_DIFFERENT_TASK); - FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED, - /* caller_uid */ - callerUid, - /* caller_activity_class_name */ - mSourceRecord.info.name, - /* target_task_top_activity_uid */ - newOrEmptyTask ? -1 : targetTopActivity.getUid(), - /* target_task_top_activity_class_name */ - newOrEmptyTask ? null : targetTopActivity.info.name, - /* target_task_is_different */ - newTask || !mSourceRecord.getTask().equals(targetTask), - /* target_activity_uid */ - r.getUid(), - /* target_activity_class_name */ - r.info.name, - /* target_intent_action */ - r.intent.getAction(), - /* target_intent_flags */ - r.intent.getFlags(), - /* action */ - action, - /* version */ - 1, - /* multi_window */ - targetTask != null && !targetTask.equals(mSourceRecord.getTask()) - && targetTask.isVisible() - ); - } + if (!checkActivitySecurityModel(r, newTask, targetTask)) { + return START_SUCCESS; } return START_SUCCESS; } + /** + * TODO(b/263368846): Shift to BackgroundActivityStartController once class is ready + * Log activity starts which violate one of the following rules of the + * activity security model (ASM): + * See go/activity-security for rationale behind the rules. + * We don't currently block, but these checks may later become blocks + * 1. Within a task, only an activity matching a top UID of the task can start activities + * 2. Only activities within a foreground task, which match a top UID of the task, can + * create a new task or bring an existing one into the foreground + */ + private boolean checkActivitySecurityModel(ActivityRecord r, boolean newTask, Task targetTask) { + // Intents with FLAG_ACTIVITY_NEW_TASK will always be considered as creating a new task + // even if the intent is delivered to an existing task. + boolean taskToFront = newTask + || (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == FLAG_ACTIVITY_NEW_TASK; + + if (mSourceRecord != null) { + boolean passesAsmChecks = true; + Task sourceTask = mSourceRecord.getTask(); + + // Don't allow launches into a new task if the current task is not foreground. + if (taskToFront) { + passesAsmChecks = sourceTask != null && sourceTask.isVisible(); + } + + Task taskToCheck = taskToFront ? sourceTask : targetTask; + passesAsmChecks = passesAsmChecks && ActivityTaskSupervisor + .doesTopActivityMatchingUidExistForAsm(taskToCheck, mSourceRecord.getUid(), + mSourceRecord); + + if (passesAsmChecks) { + return true; + } + } + + // BAL exception only allowed for new tasks + if (taskToFront) { + if (mBalCode == BAL_ALLOW_ALLOWLISTED_COMPONENT + || mBalCode == BAL_ALLOW_BAL_PERMISSION + || mBalCode == BAL_ALLOW_PENDING_INTENT) { + return true; + } + } + + // BAL Exception allowed in all cases + if (mBalCode == BAL_ALLOW_ALLOWLISTED_UID) { + return true; + } + + // TODO(b/230590090): Revisit this - ideally we would not rely on visibility, but rather + // have an explicit api for activities to opt-out of ASM protection if they need to. + if (mBalCode == BAL_ALLOW_VISIBLE_WINDOW) { + return true; + } + + // ASM rules have failed. Log why + ActivityRecord targetTopActivity = targetTask == null ? null + : targetTask.getActivity(ar -> + !ar.isState(FINISHING) && !ar.isAlwaysOnTop()); + + Slog.i(TAG, "Launching r: " + r + + " from background: " + mSourceRecord + + ". New task: " + newTask + + ". Top activity: " + targetTopActivity); + + int action = newTask || mSourceRecord == null + ? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_NEW_TASK + : (mSourceRecord.getTask().equals(targetTask) + ? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_SAME_TASK + : FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_DIFFERENT_TASK); + + FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED, + /* caller_uid */ + mSourceRecord != null ? mSourceRecord.getUid() : -1, + /* caller_activity_class_name */ + mSourceRecord != null ? mSourceRecord.info.name : null, + /* target_task_top_activity_uid */ + targetTopActivity != null ? targetTopActivity.getUid() : -1, + /* target_task_top_activity_class_name */ + targetTopActivity != null ? targetTopActivity.info.name : null, + /* target_task_is_different */ + newTask || mSourceRecord == null || targetTask == null + || !targetTask.equals(mSourceRecord.getTask()), + /* target_activity_uid */ + r.getUid(), + /* target_activity_class_name */ + r.info.name, + /* target_intent_action */ + r.intent.getAction(), + /* target_intent_flags */ + mLaunchFlags, + /* action */ + action, + /* version */ + 1, + /* multi_window - we have our source not in the target task, but both are visible */ + targetTask != null && mSourceRecord != null + && !targetTask.equals(mSourceRecord.getTask()) && targetTask.isVisible() + ); + + return false; + } + /** * Returns whether embedding of {@code starting} is allowed. * diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index b1bc52a846f19..407ffd055113f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -51,6 +51,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; +import static com.android.server.wm.ActivityRecord.State.FINISHING; import static com.android.server.wm.ActivityRecord.State.PAUSED; import static com.android.server.wm.ActivityRecord.State.PAUSING; import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS; @@ -157,6 +158,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; +import java.util.function.Predicate; // TODO: This class has become a dumping ground. Let's // - Move things relating to the hierarchy to RootWindowContainer @@ -1629,10 +1631,11 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // cleared the calling identify. If so, we infer we do not need further restrictions here. // TODO(b/263368846) Move to live with the rest of the ASM logic. if (callingUid != SYSTEM_UID) { - ActivityRecord topActivity = task.getTopNonFinishingActivity(); - boolean passesAsmChecks = topActivity != null - && topActivity.getUid() == callingUid; + boolean passesAsmChecks = doesTopActivityMatchingUidExistForAsm(task, callingUid, + null); if (!passesAsmChecks) { + ActivityRecord topActivity = task.getActivity(ar -> + !ar.isState(FINISHING) && !ar.isAlwaysOnTop()); Slog.i(TAG, "Finishing task from background. t: " + task); FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED, /* caller_uid */ @@ -1677,6 +1680,57 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } } + /** + * For the purpose of ASM, ‘Top UID” for a task is defined as an activity UID + * 1. Which is top of the stack in z-order + * a. Excluding any activities with the flag ‘isAlwaysOnTop’ and + * b. Excluding any activities which are `finishing` + * 2. Or top of an adjacent task fragment to (1) + * + * The 'sourceRecord' can be considered top even if it is 'finishing' + * + * TODO(b/263368846) Shift to BackgroundActivityStartController once class is ready + */ + @Nullable + static boolean doesTopActivityMatchingUidExistForAsm(@Nullable Task task, + int uid, @Nullable ActivityRecord sourceRecord) { + // If the source is visible, consider it 'top'. + if (sourceRecord != null && sourceRecord.isVisible()) { + return true; + } + + // Consider the source activity, whether or not it is finishing. Do not consider any other + // finishing activity. + Predicate topOfStackPredicate = (ar) -> ar.equals(sourceRecord) + || (!ar.isState(FINISHING) && !ar.isAlwaysOnTop()); + + // Check top of stack (or the first task fragment for embedding). + ActivityRecord topActivity = task.getActivity(topOfStackPredicate); + if (topActivity == null) { + return false; + } + + if (topActivity.getUid() == uid) { + return true; + } + + // Even if the top activity is not a match, we may be in an embedded activity scenario with + // an adjacent task fragment. Get the second fragment. + TaskFragment taskFragment = topActivity.getTaskFragment(); + if (taskFragment == null) { + return false; + } + + TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment(); + if (adjacentTaskFragment == null) { + return false; + } + + // Check the second fragment. + topActivity = adjacentTaskFragment.getActivity(topOfStackPredicate); + return topActivity != null && topActivity.getUid() == uid; + } + void cleanUpRemovedTaskLocked(Task task, boolean killProcess, boolean removeFromRecents) { if (removeFromRecents) { mRecentTasks.remove(task);