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;