Merge "ASM Rule and Debug Updates." into main
diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
index f1a2159..db27f60 100644
--- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
+++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
@@ -43,7 +43,7 @@
     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 = 8;
+    static final int ASM_VERSION = 9;
 
     private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
     private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f6d77ea..d6f52b8 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2053,8 +2053,8 @@
         }
 
         if (!mSupervisor.getBackgroundActivityLaunchController().checkActivityAllowedToStart(
-                mSourceRecord, r, newTask, targetTask, mLaunchFlags, mBalCode, mCallingUid,
-                mRealCallingUid)) {
+                mSourceRecord, r, newTask, avoidMoveToFront(), targetTask, mLaunchFlags, mBalCode,
+                mCallingUid, mRealCallingUid)) {
             return START_ABORTED;
         }
 
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 0f36d8e..39dd77e 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -57,6 +57,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Process;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.ArraySet;
@@ -1042,8 +1043,9 @@
      * create a new task or bring an existing one into the foreground
      */
     boolean checkActivityAllowedToStart(@Nullable ActivityRecord sourceRecord,
-            @NonNull ActivityRecord targetRecord, boolean newTask, @NonNull Task targetTask,
-            int launchFlags, int balCode, int callingUid, int realCallingUid) {
+            @NonNull ActivityRecord targetRecord, boolean newTask, boolean avoidMoveTaskToFront,
+            @Nullable Task targetTask, int launchFlags, int balCode, int callingUid,
+            int realCallingUid) {
         // BAL Exception allowed in all cases
         if (balCode == BAL_ALLOW_ALLOWLISTED_UID) {
             return true;
@@ -1067,14 +1069,36 @@
         }
 
         if (balCode == BAL_ALLOW_GRACE_PERIOD) {
+            // Allow if launching into new task, and caller matches most recently finished activity
             if (taskToFront && mTopFinishedActivity != null
                     && mTopFinishedActivity.mUid == callingUid) {
                 return true;
-            } else if (!taskToFront) {
-                FinishedActivityEntry finishedEntry =
-                        mTaskIdToFinishedActivity.get(targetTask.mTaskId);
-                if (finishedEntry != null && finishedEntry.mUid == callingUid) {
-                    return true;
+            }
+
+            // 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 =
+                            mTaskIdToFinishedActivity.get(targetTask.mTaskId);
+                    if (finishedEntry != null && finishedEntry.mUid == callingUid) {
+                        return true;
+                    }
+                }
+
+                if (sourceRecord != null) {
+                    FinishedActivityEntry finishedEntry =
+                            mTaskIdToFinishedActivity.get(sourceRecord.getTask().mTaskId);
+                    if (finishedEntry != null && finishedEntry.mUid == callingUid) {
+                        return true;
+                    }
                 }
             }
         }
@@ -1098,7 +1122,7 @@
                 bas = isTopActivityMatchingUidAbsentForAsm(taskToCheck, sourceRecord.getUid(),
                         sourceRecord);
             }
-        } else if (!taskToFront) {
+        } else if (targetTask != null && (!taskToFront || avoidMoveTaskToFront)) {
             // We don't have a sourceRecord, and we're launching into an existing task.
             // Allow if callingUid is top of stack.
             bas = isTopActivityMatchingUidAbsentForAsm(targetTask, callingUid,
@@ -1111,12 +1135,14 @@
 
         // ASM rules have failed. Log why
         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,
-            int realCallingUid, boolean newTask, Task targetTask, ActivityRecord targetRecord,
-            @BalCode int balCode, int launchFlags, BlockActivityStart bas, boolean taskToFront) {
+            int realCallingUid, boolean newTask, boolean avoidMoveTaskToFront, Task targetTask,
+            ActivityRecord targetRecord, @BalCode int balCode, int launchFlags,
+            BlockActivityStart bas, boolean taskToFront) {
 
         ActivityRecord targetTopActivity = targetTask == null ? null
                 : targetTask.getActivity(ar -> !ar.finishing && !ar.isAlwaysOnTop());
@@ -1133,7 +1159,7 @@
 
         String asmDebugInfo = getDebugInfoForActivitySecurity("Launch", sourceRecord,
                 targetRecord, targetTask, targetTopActivity, realCallingUid, balCode,
-                blockActivityStartAndFeatureEnabled, taskToFront);
+                blockActivityStartAndFeatureEnabled, taskToFront, avoidMoveTaskToFront);
 
         FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
                 /* caller_uid */
@@ -1265,7 +1291,7 @@
 
             Slog.i(TAG, getDebugInfoForActivitySecurity("Clear Top", sourceRecord, targetRecord,
                     targetTask, targetTaskTop, realCallingUid, balCode, shouldBlockActivityStart,
-                    /* taskToFront */ true));
+                    /* taskToFront */ true, /* avoidMoveTaskToFront */ false));
         }
     }
 
@@ -1379,7 +1405,7 @@
     private BlockActivityStart isTopActivityMatchingUidAbsentForAsm(@NonNull Task task,
             int uid, @Nullable ActivityRecord sourceRecord) {
         // If the source is visible, consider it 'top'.
-        if (sourceRecord != null && sourceRecord.isVisible()) {
+        if (sourceRecord != null && sourceRecord.isVisibleRequested()) {
             return new BlockActivityStart(false, false);
         }
 
@@ -1389,6 +1415,12 @@
             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
         // finishing activity.
         Predicate<ActivityRecord> topOfStackPredicate = (ar) -> ar.equals(sourceRecord)
@@ -1480,27 +1512,26 @@
             @Nullable ActivityRecord sourceRecord, @NonNull ActivityRecord targetRecord,
             @Nullable Task targetTask, @Nullable ActivityRecord targetTopActivity,
             int realCallingUid, @BalCode int balCode,
-            boolean blockActivityStartAndFeatureEnabled, boolean taskToFront) {
+            boolean blockActivityStartAndFeatureEnabled, boolean taskToFront,
+            boolean avoidMoveTaskToFront) {
         final String prefix = "[ASM] ";
         Function<ActivityRecord, String> recordToString = (ar) -> {
             if (ar == null) {
                 return null;
             }
-            return (ar == sourceRecord ? " [source]=> "
+
+            return (ar == sourceRecord ?        " [source]=> "
                     : ar == targetTopActivity ? " [ top  ]=> "
-                    : ar == targetRecord ? " [target]=> "
-                    : "         => ")
-                    + ar
-                    + " :: visible=" + ar.isVisible()
-                    + ", finishing=" + ar.isFinishing()
-                    + ", alwaysOnTop=" + ar.isAlwaysOnTop()
-                    + ", taskFragment=" + ar.getTaskFragment();
+                    : ar == targetRecord ?      " [target]=> "
+                    :                           "         => ")
+                    + getDebugStringForActivityRecord(ar);
         };
 
         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);
+        joiner.add(prefix + "System Time: " + SystemClock.uptimeMillis());
 
         boolean targetTaskMatchesSourceTask = targetTask != null
                 && sourceRecord != null && sourceRecord.getTask() == targetTask;
@@ -1512,6 +1543,8 @@
             joiner.add(prefix + "Real Calling Uid Package: " + realCallingPackage);
         } else {
             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) {
                 joiner.add(prefix + "Source/Target Task: " + sourceRecord.getTask());
                 joiner.add(prefix + "Source/Target Task Stack: ");
@@ -1536,7 +1569,30 @@
         joiner.add(prefix + "Target Record: " + recordToString.apply(targetRecord));
         joiner.add(prefix + "Intent: " + targetRecord.intent);
         joiner.add(prefix + "TaskToFront: " + taskToFront);
+        joiner.add(prefix + "AvoidMoveToFront: " + avoidMoveTaskToFront);
         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 ------");
         return joiner.toString();
@@ -1620,7 +1676,7 @@
             return;
         }
 
-        if (!finishActivity.mVisibleRequested
+        if (!finishActivity.isVisibleRequested()
                 && finishActivity != finishActivity.getTask().getTopMostActivity()) {
             return;
         }
@@ -1666,10 +1722,22 @@
         }
     }
 
+    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 {
         int mUid;
         int mTaskId;
         int mLaunchCount;
+        String mDebugInfo;
 
         FinishedActivityEntry(ActivityRecord ar) {
             FinishedActivityEntry entry = mTaskIdToFinishedActivity.get(ar.getTask().mTaskId);
@@ -1677,6 +1745,7 @@
             this.mUid = ar.getUid();
             this.mTaskId = taskId;
             this.mLaunchCount = entry == null || !ar.isUid(entry.mUid) ? 1 : entry.mLaunchCount + 1;
+            this.mDebugInfo = getDebugStringForActivityRecord(ar);
 
             mService.mH.postDelayed(() -> {
                 synchronized (mService.mGlobalLock) {