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

Commit fbbbef4e authored by Hani Kazmi's avatar Hani Kazmi
Browse files

Update ASM rules based on feedback.

This change updates the rules based on droidfood findings.

1. BAL_ALLOW_SAW_PERMISSION now allows ASM bypass for new tasks. Some
   apps (i.e, alarm apps) use display over other window permissions to
   show the alarm activity even if the alarm task is not in the
   foreground. As users must explicitely grant this permission, we do
   not consider it a risk.

2. BAL_ALLOW_VISIBLE_WINDOW now only allows ASM bypass for new tasks. If
   an app requires a bypass within a task, it can request the new flag
   we are introducing in ag/20882611.

3. Do not clear activities from top of task if launcher or launched
   activity matches top activity. This is relaxed rule from before,
   where only the launched was considered. We allow this to allow
   pending intents to be passed from A to B in the case below, and still
   fired from A without creating unpredictable back behaviour. As A
   passes the intent to B (and launched B), we assume there is a
   level of trust between A and B.

B
A
----
Bottom

Bug: 263368846
Test: atest BackgroundActivityLaunchTest ActivitySecurityModelTest
Change-Id: Ic698d27b35023063f3c7214414bee2b9298f4fb3
parent 0562cb99
Loading
Loading
Loading
Loading
+58 −49
Original line number Original line Diff line number Diff line
@@ -80,6 +80,7 @@ import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_DEFAULT;
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_PENDING_INTENT;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_SAW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
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.BackgroundActivityStartController.BAL_BLOCK;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
@@ -148,6 +149,7 @@ import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.DateFormat;
import java.util.Date;
import java.util.Date;
import java.util.function.Predicate;


/**
/**
 * Controller for interpreting how and then launching an activity.
 * Controller for interpreting how and then launching an activity.
@@ -189,6 +191,7 @@ class ActivityStarter {
    ActivityRecord mStartActivity;
    ActivityRecord mStartActivity;
    private Intent mIntent;
    private Intent mIntent;
    private int mCallingUid;
    private int mCallingUid;
    private int mRealCallingUid;
    private ActivityOptions mOptions;
    private ActivityOptions mOptions;


    // If it is BAL_BLOCK, background activity can only be started in an existing task that contains
    // If it is BAL_BLOCK, background activity can only be started in an existing task that contains
@@ -611,6 +614,7 @@ class ActivityStarter {
        mStartActivity = starter.mStartActivity;
        mStartActivity = starter.mStartActivity;
        mIntent = starter.mIntent;
        mIntent = starter.mIntent;
        mCallingUid = starter.mCallingUid;
        mCallingUid = starter.mCallingUid;
        mRealCallingUid = starter.mRealCallingUid;
        mOptions = starter.mOptions;
        mOptions = starter.mOptions;
        mBalCode = starter.mBalCode;
        mBalCode = starter.mBalCode;


@@ -1272,7 +1276,7 @@ class ActivityStarter {


        mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
        mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
                request.voiceInteractor, startFlags, checkedOptions,
                request.voiceInteractor, startFlags, checkedOptions,
                inTask, inTaskFragment, balCode, intentGrants);
                inTask, inTaskFragment, balCode, intentGrants, realCallingUid);


        if (request.outActivity != null) {
        if (request.outActivity != null) {
            request.outActivity[0] = mLastStartActivityRecord;
            request.outActivity[0] = mLastStartActivityRecord;
@@ -1423,7 +1427,7 @@ class ActivityStarter {
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, ActivityOptions options, Task inTask,
            int startFlags, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment, @BalCode int balCode,
            TaskFragment inTaskFragment, @BalCode int balCode,
            NeededUriGrants intentGrants) {
            NeededUriGrants intentGrants, int realCallingUid) {
        int result = START_CANCELED;
        int result = START_CANCELED;
        final Task startedActivityRootTask;
        final Task startedActivityRootTask;


@@ -1443,7 +1447,7 @@ class ActivityStarter {
                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
                result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                        startFlags, options, inTask, inTaskFragment, balCode,
                        startFlags, options, inTask, inTaskFragment, balCode,
                        intentGrants);
                        intentGrants, realCallingUid);
            } finally {
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                startedActivityRootTask = handleStartResult(r, options, result, newTransition,
                startedActivityRootTask = handleStartResult(r, options, result, newTransition,
@@ -1590,9 +1594,9 @@ class ActivityStarter {
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, ActivityOptions options, Task inTask,
            int startFlags, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment, @BalCode int balCode,
            TaskFragment inTaskFragment, @BalCode int balCode,
            NeededUriGrants intentGrants) {
            NeededUriGrants intentGrants, int realCallingUid) {
        setInitialState(r, options, inTask, inTaskFragment, startFlags, sourceRecord,
        setInitialState(r, options, inTask, inTaskFragment, startFlags, sourceRecord,
                voiceSession, voiceInteractor, balCode);
                voiceSession, voiceInteractor, balCode, realCallingUid);


        computeLaunchingTaskFlags();
        computeLaunchingTaskFlags();
        mIntent.setFlags(mLaunchFlags);
        mIntent.setFlags(mLaunchFlags);
@@ -1938,7 +1942,9 @@ class ActivityStarter {
        if (taskToFront) {
        if (taskToFront) {
            if (mBalCode == BAL_ALLOW_ALLOWLISTED_COMPONENT
            if (mBalCode == BAL_ALLOW_ALLOWLISTED_COMPONENT
                    || mBalCode == BAL_ALLOW_PERMISSION
                    || mBalCode == BAL_ALLOW_PERMISSION
                    || mBalCode == BAL_ALLOW_PENDING_INTENT) {
                    || mBalCode == BAL_ALLOW_PENDING_INTENT
                    || mBalCode == BAL_ALLOW_SAW_PERMISSION
                    || mBalCode == BAL_ALLOW_VISIBLE_WINDOW) {
                return true;
                return true;
            }
            }
        }
        }
@@ -1948,12 +1954,6 @@ class ActivityStarter {
            return true;
            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
        // ASM rules have failed. Log why
        ActivityRecord targetTopActivity = targetTask == null ? null
        ActivityRecord targetTopActivity = targetTask == null ? null
                : targetTask.getActivity(ar ->
                : targetTask.getActivity(ar ->
@@ -1988,7 +1988,7 @@ class ActivityStarter {
                /* action */
                /* action */
                action,
                action,
                /* version */
                /* version */
                3,
                4,
                /* multi_window - we have our source not in the target task, but both are visible */
                /* multi_window - we have our source not in the target task, but both are visible */
                targetTask != null && mSourceRecord != null
                targetTask != null && mSourceRecord != null
                        && !targetTask.equals(mSourceRecord.getTask()) && targetTask.isVisible(),
                        && !targetTask.equals(mSourceRecord.getTask()) && targetTask.isVisible(),
@@ -2106,7 +2106,8 @@ class ActivityStarter {
                reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants);
                reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants);


        if (mAddingToTask) {
        if (mAddingToTask) {
            clearTopIfNeeded(targetTask, mCallingUid, mStartActivity.getUid(), mLaunchFlags);
            clearTopIfNeeded(targetTask, mCallingUid, mRealCallingUid, mStartActivity.getUid(),
                    mLaunchFlags);
            return START_SUCCESS;
            return START_SUCCESS;
        }
        }


@@ -2139,38 +2140,45 @@ class ActivityStarter {
    }
    }


    /**
    /**
     * If the top activity uid does not match the launched activity, and the launch was not
     * If the top activity uid does not match the launching or launched activity, and the launch was
     * requested from the top uid, we want to clear out all non matching activities to prevent the
     * not requested from the top uid, we want to clear out all non matching activities to prevent
     * top activity being sandwiched.
     * the top activity being sandwiched.
     *
     * Both creator and sender UID are considered for the launching activity.
     */
     */
    private void clearTopIfNeeded(@NonNull Task targetTask, int callingUid, int startingUid,
    private void clearTopIfNeeded(@NonNull Task targetTask, int callingUid, int realCallingUid,
            int launchFlags) {
            int startingUid, int launchFlags) {
        if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != FLAG_ACTIVITY_NEW_TASK) {
        if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != FLAG_ACTIVITY_NEW_TASK) {
            // Launch is from the same task, so must be a top or privileged UID
            // Launch is from the same task, so must be a top or privileged UID
            return;
            return;
        }
        }


        ActivityRecord targetTaskTop = targetTask.getTopNonFinishingActivity();
        Predicate<ActivityRecord> isLaunchingOrLaunched = ar -> !ar.finishing && (ar.isUid(
        if (targetTaskTop != null && targetTaskTop.getUid() != startingUid) {
                startingUid) || ar.isUid(callingUid) || ar.isUid(realCallingUid));

        // Return early if we know for sure we won't need to clear any activities by just checking
        // the top activity.
        ActivityRecord targetTaskTop = targetTask.getTopMostActivity();
        if (targetTaskTop == null || isLaunchingOrLaunched.test(targetTaskTop)) {
            return;
        }

        // Find the first activity which matches a safe UID and is not finishing. Clear everything
        // above it
        boolean shouldBlockActivityStart = ActivitySecurityModelFeatureFlags
        boolean shouldBlockActivityStart = ActivitySecurityModelFeatureFlags
                .shouldRestrictActivitySwitch(callingUid);
                .shouldRestrictActivitySwitch(callingUid);
        int[] finishCount = new int[0];
        int[] finishCount = new int[0];
        if (shouldBlockActivityStart) {
        if (shouldBlockActivityStart) {
                ActivityRecord activity = targetTask.getActivity(
            ActivityRecord activity = targetTask.getActivity(isLaunchingOrLaunched);
                        ar -> !ar.finishing && ar.isUid(startingUid));

            if (activity == null) {
            if (activity == null) {
                // mStartActivity is not in task, so clear everything
                // mStartActivity is not in task, so clear everything
                activity = mStartActivity;
                activity = mStartActivity;
            }
            }


            finishCount = new int[1];
            finishCount = new int[1];
                if (activity != null) {
            targetTask.performClearTop(activity, launchFlags, finishCount);
            targetTask.performClearTop(activity, launchFlags, finishCount);
                }

            if (finishCount[0] > 0) {
            if (finishCount[0] > 0) {
                    Slog.w(TAG, "Clearing top n: " + finishCount[0] + " activities from task t: "
                Slog.w(TAG, "Cleared top n: " + finishCount[0] + " activities from task t: "
                        + targetTask + " not matching top uid: " + callingUid);
                        + targetTask + " not matching top uid: " + callingUid);
            }
            }
        }
        }
@@ -2185,7 +2193,6 @@ class ActivityStarter {
                    Toast.LENGTH_SHORT).show());
                    Toast.LENGTH_SHORT).show());
        }
        }
    }
    }
    }


    /**
    /**
     * Check if the activity being launched is the same as the one currently at the top and it
     * Check if the activity being launched is the same as the one currently at the top and it
@@ -2376,6 +2383,7 @@ class ActivityStarter {
        mStartActivity = null;
        mStartActivity = null;
        mIntent = null;
        mIntent = null;
        mCallingUid = -1;
        mCallingUid = -1;
        mRealCallingUid = -1;
        mOptions = null;
        mOptions = null;
        mBalCode = BAL_ALLOW_DEFAULT;
        mBalCode = BAL_ALLOW_DEFAULT;


@@ -2422,13 +2430,14 @@ class ActivityStarter {
    private void setInitialState(ActivityRecord r, ActivityOptions options, Task inTask,
    private void setInitialState(ActivityRecord r, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment, int startFlags,
            TaskFragment inTaskFragment, int startFlags,
            ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession,
            ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession,
            IVoiceInteractor voiceInteractor, @BalCode int balCode) {
            IVoiceInteractor voiceInteractor, @BalCode int balCode, int realCallingUid) {
        reset(false /* clearRequest */);
        reset(false /* clearRequest */);


        mStartActivity = r;
        mStartActivity = r;
        mIntent = r.intent;
        mIntent = r.intent;
        mOptions = options;
        mOptions = options;
        mCallingUid = r.launchedFromUid;
        mCallingUid = r.launchedFromUid;
        mRealCallingUid = realCallingUid;
        mSourceRecord = sourceRecord;
        mSourceRecord = sourceRecord;
        mSourceRootTask = mSourceRecord != null ? mSourceRecord.getRootTask() : null;
        mSourceRootTask = mSourceRecord != null ? mSourceRecord.getRootTask() : null;
        mVoiceSession = voiceSession;
        mVoiceSession = voiceSession;
+2 −1
Original line number Original line Diff line number Diff line
@@ -1781,6 +1781,7 @@ public class ActivityStarterTests extends WindowTestsBase {
        starter.startActivityInner(target, source, null /* voiceSession */,
        starter.startActivityInner(target, source, null /* voiceSession */,
                null /* voiceInteractor */, 0 /* startFlags */,
                null /* voiceInteractor */, 0 /* startFlags */,
                options, inTask, inTaskFragment,
                options, inTask, inTaskFragment,
                BackgroundActivityStartController.BAL_ALLOW_DEFAULT, null /* intentGrants */);
                BackgroundActivityStartController.BAL_ALLOW_DEFAULT, null /* intentGrants */,
                -1 /* realCallingUid */);
    }
    }
}
}
+4 −2
Original line number Original line Diff line number Diff line
@@ -189,7 +189,8 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
                /* inTask */null,
                /* inTask */null,
                /* inTaskFragment */ null,
                /* inTaskFragment */ null,
                /* balCode */ BackgroundActivityStartController.BAL_ALLOW_DEFAULT,
                /* balCode */ BackgroundActivityStartController.BAL_ALLOW_DEFAULT,
                /* intentGrants */null);
                /* intentGrants */null,
                /* realCaiingUid */ -1);


        assertEquals(result, START_ABORTED);
        assertEquals(result, START_ABORTED);
    }
    }
@@ -213,7 +214,8 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
                /* inTask= */null,
                /* inTask= */null,
                /* inTaskFragment= */ null,
                /* inTaskFragment= */ null,
                /* balCode= */ BackgroundActivityStartController.BAL_ALLOW_DEFAULT,
                /* balCode= */ BackgroundActivityStartController.BAL_ALLOW_DEFAULT,
                /* intentGrants= */null);
                /* intentGrants= */null,
                /* realCaiingUid */ -1);


        assertEquals(result, START_ABORTED);
        assertEquals(result, START_ABORTED);
    }
    }