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

Commit 07a30c91 authored by Hani Kazmi's avatar Hani Kazmi
Browse files

ASM Rule and Debug Updates.

This change updates ASM rules based on gathered data, and improves debug
information.

1. An activity in the background can fire a PendingIntent, with affinity
   for an existing task. The BAL code is BAL_BLOCK, but is not blocked
   by BAL rules due to the affinity. We now also allow this case for ASM
   by checking avoidMoveToFront.

2. An actiivty can launch multiple GrantPermissionActitities in quick
   succession. If the launcher is visible, this was allowed. However, if
   the launcher was not (e.g, a URIHandler redirect), it would be
   blocked by ASM. We now allow it in the redirect case as well by
   considering the visibiltiy of all activities in the task.

Bug: 230590090
Test: atest BackgroundActivityLaunchTest ActivitySecurityModelTest
Change-Id: I221620ec35f4ddee1425beaa2898d85389cccdae
parent b431dc66
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -43,7 +43,7 @@ class ActivitySecurityModelFeatureFlags {
    static final String DOC_LINK = "go/android-asm";
    static final String DOC_LINK = "go/android-asm";


    /** Used to determine which version of the ASM logic was used in logs while we iterate */
    /** Used to determine which version of the ASM logic was used in logs while we iterate */
    static final int ASM_VERSION = 8;
    static final int ASM_VERSION = 9;


    private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
    private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
    private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
    private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
+2 −2
Original line number Original line Diff line number Diff line
@@ -2053,8 +2053,8 @@ class ActivityStarter {
        }
        }


        if (!mSupervisor.getBackgroundActivityLaunchController().checkActivityAllowedToStart(
        if (!mSupervisor.getBackgroundActivityLaunchController().checkActivityAllowedToStart(
                mSourceRecord, r, newTask, targetTask, mLaunchFlags, mBalCode, mCallingUid,
                mSourceRecord, r, newTask, avoidMoveToFront(), targetTask, mLaunchFlags, mBalCode,
                mRealCallingUid)) {
                mCallingUid, mRealCallingUid)) {
            return START_ABORTED;
            return START_ABORTED;
        }
        }


+93 −24
Original line number Original line Diff line number Diff line
@@ -57,6 +57,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.os.Process;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.ArraySet;
@@ -1042,8 +1043,9 @@ public class BackgroundActivityStartController {
     * create a new task or bring an existing one into the foreground
     * create a new task or bring an existing one into the foreground
     */
     */
    boolean checkActivityAllowedToStart(@Nullable ActivityRecord sourceRecord,
    boolean checkActivityAllowedToStart(@Nullable ActivityRecord sourceRecord,
            @NonNull ActivityRecord targetRecord, boolean newTask, @NonNull Task targetTask,
            @NonNull ActivityRecord targetRecord, boolean newTask, boolean avoidMoveTaskToFront,
            int launchFlags, int balCode, int callingUid, int realCallingUid) {
            @Nullable Task targetTask, int launchFlags, int balCode, int callingUid,
            int realCallingUid) {
        // BAL Exception allowed in all cases
        // BAL Exception allowed in all cases
        if (balCode == BAL_ALLOW_ALLOWLISTED_UID) {
        if (balCode == BAL_ALLOW_ALLOWLISTED_UID) {
            return true;
            return true;
@@ -1067,16 +1069,38 @@ public class BackgroundActivityStartController {
        }
        }


        if (balCode == BAL_ALLOW_GRACE_PERIOD) {
        if (balCode == BAL_ALLOW_GRACE_PERIOD) {
            // Allow if launching into new task, and caller matches most recently finished activity
            if (taskToFront && mTopFinishedActivity != null
            if (taskToFront && mTopFinishedActivity != null
                    && mTopFinishedActivity.mUid == callingUid) {
                    && mTopFinishedActivity.mUid == callingUid) {
                return true;
                return true;
            } else if (!taskToFront) {
            }

            // Launching into existing task - allow if matches most recently finished activity
            // within the task.
            // We can reach here multiple ways:
            // 1. activity in fg fires intent (taskToFront = false, sourceRecord is available)
            // 2. activity in bg fires intent (taskToFront = false, sourceRecord is available)
            // 3. activity in bg fires intent with NEW_FLAG (taskToFront = true,
            //         avoidMoveTaskToFront = true, sourceRecord is available)
            // 4. activity in bg fires PI (taskToFront = true, avoidMoveTaskToFront = true,
            //         sourceRecord is not available, targetTask may be available)
            if (!taskToFront || avoidMoveTaskToFront) {
                if (targetTask != null) {
                    FinishedActivityEntry finishedEntry =
                    FinishedActivityEntry finishedEntry =
                            mTaskIdToFinishedActivity.get(targetTask.mTaskId);
                            mTaskIdToFinishedActivity.get(targetTask.mTaskId);
                    if (finishedEntry != null && finishedEntry.mUid == callingUid) {
                    if (finishedEntry != null && finishedEntry.mUid == callingUid) {
                        return true;
                        return true;
                    }
                    }
                }
                }

                if (sourceRecord != null) {
                    FinishedActivityEntry finishedEntry =
                            mTaskIdToFinishedActivity.get(sourceRecord.getTask().mTaskId);
                    if (finishedEntry != null && finishedEntry.mUid == callingUid) {
                        return true;
                    }
                }
            }
        }
        }


        BlockActivityStart bas = null;
        BlockActivityStart bas = null;
@@ -1098,7 +1122,7 @@ public class BackgroundActivityStartController {
                bas = isTopActivityMatchingUidAbsentForAsm(taskToCheck, sourceRecord.getUid(),
                bas = isTopActivityMatchingUidAbsentForAsm(taskToCheck, sourceRecord.getUid(),
                        sourceRecord);
                        sourceRecord);
            }
            }
        } else if (!taskToFront) {
        } else if (targetTask != null && (!taskToFront || avoidMoveTaskToFront)) {
            // We don't have a sourceRecord, and we're launching into an existing task.
            // We don't have a sourceRecord, and we're launching into an existing task.
            // Allow if callingUid is top of stack.
            // Allow if callingUid is top of stack.
            bas = isTopActivityMatchingUidAbsentForAsm(targetTask, callingUid,
            bas = isTopActivityMatchingUidAbsentForAsm(targetTask, callingUid,
@@ -1111,12 +1135,14 @@ public class BackgroundActivityStartController {


        // ASM rules have failed. Log why
        // ASM rules have failed. Log why
        return logAsmFailureAndCheckFeatureEnabled(sourceRecord, callingUid, realCallingUid,
        return logAsmFailureAndCheckFeatureEnabled(sourceRecord, callingUid, realCallingUid,
                newTask, targetTask, targetRecord, balCode, launchFlags, bas, taskToFront);
                newTask, avoidMoveTaskToFront, targetTask, targetRecord, balCode, launchFlags,
                bas, taskToFront);
    }
    }


    private boolean logAsmFailureAndCheckFeatureEnabled(ActivityRecord sourceRecord, int callingUid,
    private boolean logAsmFailureAndCheckFeatureEnabled(ActivityRecord sourceRecord, int callingUid,
            int realCallingUid, boolean newTask, Task targetTask, ActivityRecord targetRecord,
            int realCallingUid, boolean newTask, boolean avoidMoveTaskToFront, Task targetTask,
            @BalCode int balCode, int launchFlags, BlockActivityStart bas, boolean taskToFront) {
            ActivityRecord targetRecord, @BalCode int balCode, int launchFlags,
            BlockActivityStart bas, boolean taskToFront) {


        ActivityRecord targetTopActivity = targetTask == null ? null
        ActivityRecord targetTopActivity = targetTask == null ? null
                : targetTask.getActivity(ar -> !ar.finishing && !ar.isAlwaysOnTop());
                : targetTask.getActivity(ar -> !ar.finishing && !ar.isAlwaysOnTop());
@@ -1133,7 +1159,7 @@ public class BackgroundActivityStartController {


        String asmDebugInfo = getDebugInfoForActivitySecurity("Launch", sourceRecord,
        String asmDebugInfo = getDebugInfoForActivitySecurity("Launch", sourceRecord,
                targetRecord, targetTask, targetTopActivity, realCallingUid, balCode,
                targetRecord, targetTask, targetTopActivity, realCallingUid, balCode,
                blockActivityStartAndFeatureEnabled, taskToFront);
                blockActivityStartAndFeatureEnabled, taskToFront, avoidMoveTaskToFront);


        FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
        FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
                /* caller_uid */
                /* caller_uid */
@@ -1265,7 +1291,7 @@ public class BackgroundActivityStartController {


            Slog.i(TAG, getDebugInfoForActivitySecurity("Clear Top", sourceRecord, targetRecord,
            Slog.i(TAG, getDebugInfoForActivitySecurity("Clear Top", sourceRecord, targetRecord,
                    targetTask, targetTaskTop, realCallingUid, balCode, shouldBlockActivityStart,
                    targetTask, targetTaskTop, realCallingUid, balCode, shouldBlockActivityStart,
                    /* taskToFront */ true));
                    /* taskToFront */ true, /* avoidMoveTaskToFront */ false));
        }
        }
    }
    }


@@ -1379,7 +1405,7 @@ public class BackgroundActivityStartController {
    private BlockActivityStart isTopActivityMatchingUidAbsentForAsm(@NonNull Task task,
    private BlockActivityStart isTopActivityMatchingUidAbsentForAsm(@NonNull Task task,
            int uid, @Nullable ActivityRecord sourceRecord) {
            int uid, @Nullable ActivityRecord sourceRecord) {
        // If the source is visible, consider it 'top'.
        // If the source is visible, consider it 'top'.
        if (sourceRecord != null && sourceRecord.isVisible()) {
        if (sourceRecord != null && sourceRecord.isVisibleRequested()) {
            return new BlockActivityStart(false, false);
            return new BlockActivityStart(false, false);
        }
        }


@@ -1389,6 +1415,12 @@ public class BackgroundActivityStartController {
            return new BlockActivityStart(false, false);
            return new BlockActivityStart(false, false);
        }
        }


        // If UID is visible in target task, allow launch
        if (task.forAllActivities((Predicate<ActivityRecord>)
                ar -> ar.isUid(uid) && ar.isVisibleRequested())) {
            return new BlockActivityStart(false, false);
        }

        // Consider the source activity, whether or not it is finishing. Do not consider any other
        // Consider the source activity, whether or not it is finishing. Do not consider any other
        // finishing activity.
        // finishing activity.
        Predicate<ActivityRecord> topOfStackPredicate = (ar) -> ar.equals(sourceRecord)
        Predicate<ActivityRecord> topOfStackPredicate = (ar) -> ar.equals(sourceRecord)
@@ -1480,27 +1512,26 @@ public class BackgroundActivityStartController {
            @Nullable ActivityRecord sourceRecord, @NonNull ActivityRecord targetRecord,
            @Nullable ActivityRecord sourceRecord, @NonNull ActivityRecord targetRecord,
            @Nullable Task targetTask, @Nullable ActivityRecord targetTopActivity,
            @Nullable Task targetTask, @Nullable ActivityRecord targetTopActivity,
            int realCallingUid, @BalCode int balCode,
            int realCallingUid, @BalCode int balCode,
            boolean blockActivityStartAndFeatureEnabled, boolean taskToFront) {
            boolean blockActivityStartAndFeatureEnabled, boolean taskToFront,
            boolean avoidMoveTaskToFront) {
        final String prefix = "[ASM] ";
        final String prefix = "[ASM] ";
        Function<ActivityRecord, String> recordToString = (ar) -> {
        Function<ActivityRecord, String> recordToString = (ar) -> {
            if (ar == null) {
            if (ar == null) {
                return null;
                return null;
            }
            }

            return (ar == sourceRecord ?        " [source]=> "
            return (ar == sourceRecord ?        " [source]=> "
                    : ar == targetTopActivity ? " [ top  ]=> "
                    : ar == targetTopActivity ? " [ top  ]=> "
                    : ar == targetRecord ?      " [target]=> "
                    : ar == targetRecord ?      " [target]=> "
                    :                           "         => ")
                    :                           "         => ")
                    + ar
                    + getDebugStringForActivityRecord(ar);
                    + " :: visible=" + ar.isVisible()
                    + ", finishing=" + ar.isFinishing()
                    + ", alwaysOnTop=" + ar.isAlwaysOnTop()
                    + ", taskFragment=" + ar.getTaskFragment();
        };
        };


        StringJoiner joiner = new StringJoiner("\n");
        StringJoiner joiner = new StringJoiner("\n");
        joiner.add(prefix + "------ Activity Security " + action + " Debug Logging Start ------");
        joiner.add(prefix + "------ Activity Security " + action + " Debug Logging Start ------");
        joiner.add(prefix + "Block Enabled: " + blockActivityStartAndFeatureEnabled);
        joiner.add(prefix + "Block Enabled: " + blockActivityStartAndFeatureEnabled);
        joiner.add(prefix + "ASM Version: " + ActivitySecurityModelFeatureFlags.ASM_VERSION);
        joiner.add(prefix + "ASM Version: " + ActivitySecurityModelFeatureFlags.ASM_VERSION);
        joiner.add(prefix + "System Time: " + SystemClock.uptimeMillis());


        boolean targetTaskMatchesSourceTask = targetTask != null
        boolean targetTaskMatchesSourceTask = targetTask != null
                && sourceRecord != null && sourceRecord.getTask() == targetTask;
                && sourceRecord != null && sourceRecord.getTask() == targetTask;
@@ -1512,6 +1543,8 @@ public class BackgroundActivityStartController {
            joiner.add(prefix + "Real Calling Uid Package: " + realCallingPackage);
            joiner.add(prefix + "Real Calling Uid Package: " + realCallingPackage);
        } else {
        } else {
            joiner.add(prefix + "Source Record: " + recordToString.apply(sourceRecord));
            joiner.add(prefix + "Source Record: " + recordToString.apply(sourceRecord));
            joiner.add(prefix + "Source Launch Package: " + sourceRecord.launchedFromPackage);
            joiner.add(prefix + "Source Launch Intent: " + sourceRecord.intent);
            if (targetTaskMatchesSourceTask) {
            if (targetTaskMatchesSourceTask) {
                joiner.add(prefix + "Source/Target Task: " + sourceRecord.getTask());
                joiner.add(prefix + "Source/Target Task: " + sourceRecord.getTask());
                joiner.add(prefix + "Source/Target Task Stack: ");
                joiner.add(prefix + "Source/Target Task Stack: ");
@@ -1536,7 +1569,30 @@ public class BackgroundActivityStartController {
        joiner.add(prefix + "Target Record: " + recordToString.apply(targetRecord));
        joiner.add(prefix + "Target Record: " + recordToString.apply(targetRecord));
        joiner.add(prefix + "Intent: " + targetRecord.intent);
        joiner.add(prefix + "Intent: " + targetRecord.intent);
        joiner.add(prefix + "TaskToFront: " + taskToFront);
        joiner.add(prefix + "TaskToFront: " + taskToFront);
        joiner.add(prefix + "AvoidMoveToFront: " + avoidMoveTaskToFront);
        joiner.add(prefix + "BalCode: " + balCodeToString(balCode));
        joiner.add(prefix + "BalCode: " + balCodeToString(balCode));
        joiner.add(prefix + "LastResumedActivity: "
                       + recordToString.apply(mService.mLastResumedActivity));

        if (mTopFinishedActivity != null) {
            joiner.add(prefix + "TopFinishedActivity: " + mTopFinishedActivity.mDebugInfo);
        }

        if (!mTaskIdToFinishedActivity.isEmpty()) {
            joiner.add(prefix + "TaskIdToFinishedActivity: ");
            mTaskIdToFinishedActivity.values().forEach(
                    (fae) -> joiner.add(prefix + "  " + fae.mDebugInfo));
        }

        if (balCode == BAL_ALLOW_VISIBLE_WINDOW || balCode == BAL_ALLOW_NON_APP_VISIBLE_WINDOW
                || balCode == BAL_ALLOW_FOREGROUND) {
            Task task = sourceRecord != null ? sourceRecord.getTask() : targetTask;
            if (task != null && task.getDisplayArea() != null) {
                joiner.add(prefix + "Tasks: ");
                task.getDisplayArea().forAllTasks((Consumer<Task>)
                        t -> joiner.add(prefix + "   T: " + t.toFullString()));
            }
        }


        joiner.add(prefix + "------ Activity Security " + action + " Debug Logging End ------");
        joiner.add(prefix + "------ Activity Security " + action + " Debug Logging End ------");
        return joiner.toString();
        return joiner.toString();
@@ -1620,7 +1676,7 @@ public class BackgroundActivityStartController {
            return;
            return;
        }
        }


        if (!finishActivity.mVisibleRequested
        if (!finishActivity.isVisibleRequested()
                && finishActivity != finishActivity.getTask().getTopMostActivity()) {
                && finishActivity != finishActivity.getTask().getTopMostActivity()) {
            return;
            return;
        }
        }
@@ -1666,10 +1722,22 @@ public class BackgroundActivityStartController {
        }
        }
    }
    }


    private static String getDebugStringForActivityRecord(ActivityRecord ar) {
        return ar
                + " :: visible=" + ar.isVisible()
                + ", visibleRequested=" + ar.isVisibleRequested()
                + ", finishing=" + ar.isFinishing()
                + ", alwaysOnTop=" + ar.isAlwaysOnTop()
                + ", lastLaunchTime=" + ar.lastLaunchTime
                + ", lastVisibleTime=" + ar.lastVisibleTime
                + ", taskFragment=" + ar.getTaskFragment();
    }

    private class FinishedActivityEntry {
    private class FinishedActivityEntry {
        int mUid;
        int mUid;
        int mTaskId;
        int mTaskId;
        int mLaunchCount;
        int mLaunchCount;
        String mDebugInfo;


        FinishedActivityEntry(ActivityRecord ar) {
        FinishedActivityEntry(ActivityRecord ar) {
            FinishedActivityEntry entry = mTaskIdToFinishedActivity.get(ar.getTask().mTaskId);
            FinishedActivityEntry entry = mTaskIdToFinishedActivity.get(ar.getTask().mTaskId);
@@ -1677,6 +1745,7 @@ public class BackgroundActivityStartController {
            this.mUid = ar.getUid();
            this.mUid = ar.getUid();
            this.mTaskId = taskId;
            this.mTaskId = taskId;
            this.mLaunchCount = entry == null || !ar.isUid(entry.mUid) ? 1 : entry.mLaunchCount + 1;
            this.mLaunchCount = entry == null || !ar.isUid(entry.mUid) ? 1 : entry.mLaunchCount + 1;
            this.mDebugInfo = getDebugStringForActivityRecord(ar);


            mService.mH.postDelayed(() -> {
            mService.mH.postDelayed(() -> {
                synchronized (mService.mGlobalLock) {
                synchronized (mService.mGlobalLock) {