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

Commit edb6afca authored by Hani Kazmi's avatar Hani Kazmi Committed by Cherrypicker Worker
Browse files

ASM - Use finishing instead of isState() and more logging

The following changes are added for go/activity-security:

1. Use `finishing` rather than `isState(FINISHING)` for detecting
   finishing activities. isState does not always return true if an
   activity finishes and the one below it immediately starts another.
2. Update toasts to be more succinct as they are limited to two lines.
   Change their duration to be longer so they are easier to read.
3. Log ASM activity if toasts are enabled, regardless of enforcement.
4. Log more information about the task environment.

Test: atest ActivitySecurityModelTest ActivitySecurityModelEmbeddingTest
BackgroundActivityLaunchTest
Bug: 269253404, 248045984

Change-Id: Ifd72b9b9762a654e5581c3861115420c4a02a4c6
(cherry picked from commit 6c60d0ba)
Merged-In: Ifd72b9b9762a654e5581c3861115420c4a02a4c6
parent 4687a546
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -42,6 +42,9 @@ class ActivitySecurityModelFeatureFlags {
    // TODO(b/230590090): Replace with public documentation once ready
    static final String DOC_LINK = "go/android-asm";

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

    private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
    private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
    private static final String KEY_ASM_RESTRICTIONS_ENABLED = KEY_ASM_PREFIX
+99 −15
Original line number Diff line number Diff line
@@ -60,7 +60,6 @@ import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TAS

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;
@@ -83,6 +82,7 @@ import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_
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_BLOCK;
import static com.android.server.wm.BackgroundActivityStartController.balCodeToString;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
@@ -150,6 +150,9 @@ import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.Date;
import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

/**
@@ -1967,8 +1970,7 @@ class ActivityStarter {

        // ASM rules have failed. Log why
        ActivityRecord targetTopActivity = targetTask == null ? null
                : targetTask.getActivity(ar ->
                        !ar.isState(FINISHING) && !ar.isAlwaysOnTop());
                : targetTask.getActivity(ar -> !ar.finishing && !ar.isAlwaysOnTop());

        int action = newTask || mSourceRecord == null
                ? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_NEW_TASK
@@ -1999,7 +2001,7 @@ class ActivityStarter {
                /* action */
                action,
                /* version */
                4,
                ActivitySecurityModelFeatureFlags.ASM_VERSION,
                /* 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(),
@@ -2011,22 +2013,25 @@ class ActivityStarter {
                    .shouldRestrictActivitySwitch(mCallingUid)
                && shouldBlockActivityStart;

        String launchedFromPackageName = r.launchedFromPackage;
        if (ActivitySecurityModelFeatureFlags.shouldShowToast(mCallingUid)) {
            String toastText = ActivitySecurityModelFeatureFlags.DOC_LINK
                    + (blockActivityStartAndFeatureEnabled ? " blocked " : " would block ")
                    + getApplicationLabel(launchedFromPackageName);
            UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
                    "Activity start from " + r.launchedFromPackage
                            + (blockActivityStartAndFeatureEnabled ? " " : " would be ")
                            + "blocked by " + ActivitySecurityModelFeatureFlags.DOC_LINK,
                    Toast.LENGTH_SHORT).show());
        }
                    toastText, Toast.LENGTH_LONG).show());

            logDebugInfoForActivitySecurity("Launch", r, targetTask, targetTopActivity,
                    blockActivityStartAndFeatureEnabled, /* taskToFront */ taskToFront);
        }

        if (blockActivityStartAndFeatureEnabled) {
            Slog.e(TAG, "Abort Launching r: " + r
            Slog.e(TAG, "[ASM] Abort Launching r: " + r
                    + " as source: "
                    + (mSourceRecord != null ? mSourceRecord : r.launchedFromPackage)
                    + (mSourceRecord != null ? mSourceRecord : launchedFromPackageName)
                    + " is in background. New task: " + newTask
                    + ". Top activity: " + targetTopActivity
                    + ". BAL Code: " + mBalCode);
                    + ". BAL Code: " + balCodeToString(mBalCode));

            return false;
        }
@@ -2034,6 +2039,82 @@ class ActivityStarter {
        return true;
    }

    private CharSequence getApplicationLabel(String packageName) {
        try {
            PackageManager packageManager = mService.mContext.getPackageManager();
            ApplicationInfo launchedFromPackageInfo = packageManager.getApplicationInfo(
                    packageName, PackageManager.ApplicationInfoFlags.of(0));
            return packageManager.getApplicationLabel(launchedFromPackageInfo);
        } catch (PackageManager.NameNotFoundException e) {
            return packageName;
        }
    }

    /** Only called when an activity launch may be blocked, which should happen very rarely */
    private void logDebugInfoForActivitySecurity(String action, ActivityRecord r, Task targetTask,
            ActivityRecord targetTopActivity, boolean blockActivityStartAndFeatureEnabled,
            boolean taskToFront) {
        final String prefix = "[ASM] ";
        Function<ActivityRecord, String> recordToString = (ar) -> {
            if (ar == null) {
                return null;
            }
            return (ar == mSourceRecord ? " [source]=> "
                    : ar == targetTopActivity ? " [ top  ]=> "
                            : ar == r ? " [target]=> "
                                    : "         => ")
                    + ar
                    + " :: visible=" + ar.isVisible()
                    + ", finishing=" + ar.isFinishing()
                    + ", alwaysOnTop=" + ar.isAlwaysOnTop()
                    + ", taskFragment=" + ar.getTaskFragment();
        };

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

        boolean targetTaskMatchesSourceTask = targetTask != null
                && mSourceRecord != null && mSourceRecord.getTask() == targetTask;

        if (mSourceRecord == null) {
            joiner.add(prefix + "Source Package: " + r.launchedFromPackage);
            String realCallingPackage = mService.mContext.getPackageManager().getNameForUid(
                    mRealCallingUid);
            joiner.add(prefix + "Real Calling Uid Package: " + realCallingPackage);
        } else {
            joiner.add(prefix + "Source Record: " + recordToString.apply(mSourceRecord));
            if (targetTaskMatchesSourceTask) {
                joiner.add(prefix + "Source/Target Task: " + mSourceRecord.getTask());
                joiner.add(prefix + "Source/Target Task Stack: ");
            } else {
                joiner.add(prefix + "Source Task: " + mSourceRecord.getTask());
                joiner.add(prefix + "Source Task Stack: ");
            }
            mSourceRecord.getTask().forAllActivities((Consumer<ActivityRecord>)
                    ar -> joiner.add(prefix + recordToString.apply(ar)));
        }

        joiner.add(prefix + "Target Task Top: " + recordToString.apply(targetTopActivity));
        if (!targetTaskMatchesSourceTask) {
            joiner.add(prefix + "Target Task: " + targetTask);
            if (targetTask != null) {
                joiner.add(prefix + "Target Task Stack: ");
                targetTask.forAllActivities((Consumer<ActivityRecord>)
                        ar -> joiner.add(prefix + recordToString.apply(ar)));
            }
        }

        joiner.add(prefix + "Target Record: " + recordToString.apply(r));
        joiner.add(prefix + "Intent: " + mIntent);
        joiner.add(prefix + "TaskToFront: " + taskToFront);
        joiner.add(prefix + "BalCode: " + balCodeToString(mBalCode));

        joiner.add(prefix + "------ Activity Security " + action + " Debug Logging End ------");
        Slog.i(TAG, joiner.toString());
    }

    /**
     * Returns whether embedding of {@code starting} is allowed.
     *
@@ -2165,8 +2246,8 @@ class ActivityStarter {
            return;
        }

        Predicate<ActivityRecord> isLaunchingOrLaunched = ar -> !ar.finishing && (ar.isUid(
                startingUid) || ar.isUid(callingUid) || ar.isUid(realCallingUid));
        Predicate<ActivityRecord> isLaunchingOrLaunched = ar -> !ar.finishing
                && (ar.isUid(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.
@@ -2202,7 +2283,10 @@ class ActivityStarter {
                            ? "Top activities cleared by "
                            : "Top activities would be cleared by ")
                            + ActivitySecurityModelFeatureFlags.DOC_LINK,
                    Toast.LENGTH_SHORT).show());
                    Toast.LENGTH_LONG).show());

            logDebugInfoForActivitySecurity("Clear Top", mStartActivity, targetTask, targetTaskTop,
                    shouldBlockActivityStart, /* taskToFront */ true);
        }
    }

+8 −6
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ 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;
@@ -1649,7 +1648,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
            wouldBlockActivitySwitchIgnoringFlags = !pair.second;
            if (wouldBlockActivitySwitchIgnoringFlags) {
                ActivityRecord topActivity =  task.getActivity(ar ->
                        !ar.isState(FINISHING) && !ar.isAlwaysOnTop());
                        !ar.finishing && !ar.isAlwaysOnTop());
                FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
                        /* caller_uid */
                        callingUid,
@@ -1672,7 +1671,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
                        /* action */
                        FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__FINISH_TASK,
                        /* version */
                        3,
                        ActivitySecurityModelFeatureFlags.ASM_VERSION,
                        /* multi_window */
                        false,
                        /* bal_code */
@@ -1700,16 +1699,19 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
                                    ? "Returning home due to "
                                    : "Would return home due to ")
                                    + ActivitySecurityModelFeatureFlags.DOC_LINK,
                            Toast.LENGTH_SHORT).show());
                            Toast.LENGTH_LONG).show());
                }

                // If the activity switch should be restricted, return home rather than the
                // previously top task, to prevent users from being confused which app they're
                // viewing
                if (restrictActivitySwitch) {
                    Slog.w(TAG, "Return to home as source uid: " + callingUid
                    Slog.w(TAG, "[ASM] Return to home as source uid: " + callingUid
                            + "is not on top of task t: " + task);
                    task.getTaskDisplayArea().moveHomeActivityToTop("taskRemoved");
                } else {
                    Slog.i(TAG, "[ASM] Would return to home as source uid: " + callingUid
                            + "is not on top of task t: " + task);
                }
            }
        } finally {
@@ -1743,7 +1745,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
        // 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());
                || (!ar.finishing && !ar.isAlwaysOnTop());

        // Check top of stack (or the first task fragment for embedding).
        ActivityRecord topActivity = task.getActivity(topOfStackPredicate);
+29 −0
Original line number Diff line number Diff line
@@ -114,6 +114,35 @@ public class BackgroundActivityStartController {
    /** Process belongs to a SDK sandbox */
    static final int BAL_ALLOW_SDK_SANDBOX = 10;

    static String balCodeToString(@BalCode int balCode) {
        switch (balCode) {
            case BAL_ALLOW_ALLOWLISTED_COMPONENT:
                return "BAL_ALLOW_ALLOWLISTED_COMPONENT";
            case BAL_ALLOW_ALLOWLISTED_UID:
                return "BAL_ALLOW_ALLOWLISTED_UID";
            case BAL_ALLOW_DEFAULT:
                return "BAL_ALLOW_DEFAULT";
            case BAL_ALLOW_FOREGROUND:
                return "BAL_ALLOW_FOREGROUND";
            case BAL_ALLOW_GRACE_PERIOD:
                return "BAL_ALLOW_GRACE_PERIOD";
            case BAL_ALLOW_PENDING_INTENT:
                return "BAL_ALLOW_PENDING_INTENT";
            case BAL_ALLOW_PERMISSION:
                return "BAL_ALLOW_PERMISSION";
            case BAL_ALLOW_SAW_PERMISSION:
                return "BAL_ALLOW_SAW_PERMISSION";
            case BAL_ALLOW_SDK_SANDBOX:
                return "BAL_ALLOW_SDK_SANDBOX";
            case BAL_ALLOW_VISIBLE_WINDOW:
                return "BAL_ALLOW_VISIBLE_WINDOW";
            case BAL_BLOCK:
                return "BAL_BLOCK";
            default:
                throw new IllegalArgumentException("Unexpected value: " + balCode);
        }
    }

    BackgroundActivityStartController(
            final ActivityTaskManagerService service, final ActivityTaskSupervisor supervisor) {
        mService = service;