Update ASM activity start rules based on new metrics.
This change updates the rules introduced in ag/19532978 based on the findings in go/asm-buckets to reduce impact on apps. Namely, the rules have been updated to: 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 These rules are not yet final, and may be updated again once we have more data from droidfood. This commit also starts to allow launches for a subset of the BAL exceptions, even if they violate the above two rules. See go/asm-buckets for the full list. BYPASS_LONG_LINES_REASON: Auto generated enum names Bug: 246738504 Test: atest ActivityStarterTests BackgroundActivityLaunchTest Test: Manually tested scenarios from go/asm-buckets. Change-Id: Ia4bc2eeb0006c94c006072a841ebba7d3bd7b99b
This commit is contained in:
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
@@ -1633,10 +1635,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 */
|
||||
@@ -1681,6 +1684,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<ActivityRecord> 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);
|
||||
|
||||
Reference in New Issue
Block a user