From 6c60d0ba151715e11a6be5e7f3764e7ab342eb32 Mon Sep 17 00:00:00 2001 From: Hani Kazmi Date: Mon, 20 Feb 2023 17:28:57 +0000 Subject: [PATCH] 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 --- .../wm/ActivitySecurityModelFeatureFlags.java | 3 + .../android/server/wm/ActivityStarter.java | 114 +++++++++++++++--- .../server/wm/ActivityTaskSupervisor.java | 14 ++- .../wm/BackgroundActivityStartController.java | 29 +++++ 4 files changed, 139 insertions(+), 21 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java index 91c4a2ff03e0b..8a4fc0db02640 100644 --- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java +++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java @@ -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 diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 32dac49102bde..48e5f79013d58 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -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 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) + 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) + 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 isLaunchingOrLaunched = ar -> !ar.finishing && (ar.isUid( - startingUid) || ar.isUid(callingUid) || ar.isUid(realCallingUid)); + Predicate 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); } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index ef47b6e5d70a2..abe71658ca23e 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -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 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); diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 587138ded8cb4..8fc3797244754 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -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;