Merge "Update ASM activity start rules based on new metrics."
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
|
||||
@@ -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<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