Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bc215298 authored by Hani Kazmi's avatar Hani Kazmi Committed by Android (Google) Code Review
Browse files

Merge "Update ASM activity start rules based on new metrics."

parents 4b3fa992 464d3717
Loading
Loading
Loading
Loading
+112 −53
Original line number Diff line number Diff line
@@ -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,42 +1858,96 @@ 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 (!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) {
            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) {
            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);
                boolean newOrEmptyTask = newTask || (targetTopActivity == null);
                int action = newTask
                + ". 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 */
                        callerUid,
                mSourceRecord != null ? mSourceRecord.getUid() : -1,
                /* caller_activity_class_name */
                        mSourceRecord.info.name,
                mSourceRecord != null ? mSourceRecord.info.name : null,
                /* target_task_top_activity_uid */
                        newOrEmptyTask ? -1 : targetTopActivity.getUid(),
                targetTopActivity != null ? targetTopActivity.getUid() : -1,
                /* target_task_top_activity_class_name */
                        newOrEmptyTask ? null : targetTopActivity.info.name,
                targetTopActivity != null ? targetTopActivity.info.name : null,
                /* target_task_is_different */
                        newTask || !mSourceRecord.getTask().equals(targetTask),
                newTask || mSourceRecord == null || targetTask == null
                        || !targetTask.equals(mSourceRecord.getTask()),
                /* target_activity_uid */
                r.getUid(),
                /* target_activity_class_name */
@@ -1894,19 +1955,17 @@ class ActivityStarter {
                /* target_intent_action */
                r.intent.getAction(),
                /* target_intent_flags */
                        r.intent.getFlags(),
                mLaunchFlags,
                /* action */
                action,
                /* version */
                1,
                        /* multi_window */
                        targetTask != null && !targetTask.equals(mSourceRecord.getTask())
                                && targetTask.isVisible()
                /* 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 START_SUCCESS;
        return false;
    }

    /**
+57 −3
Original line number Diff line number Diff line
@@ -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);