Merge "Add ownership for BackgroundActivityStartController."
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index b0ad5a9..21385bc 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -90,6 +90,8 @@
 
     boolean mCheckedForSetup = false;
 
+    private final BackgroundActivityStartController mBalController;
+
     /**
      * TODO(b/64750076): Capture information necessary for dump and
      * {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object
@@ -112,6 +114,7 @@
         mFactory.setController(this);
         mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service.mGlobalLock,
                 service.mH);
+        mBalController = new BackgroundActivityStartController(mService, mSupervisor);
     }
 
     /**
@@ -581,4 +584,8 @@
             pw.println("(nothing)");
         }
     }
+
+    BackgroundActivityStartController getBackgroundActivityLaunchController() {
+        return mBalController;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index ec9babf..410da73 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.app.Activity.RESULT_CANCELED;
 import static android.app.ActivityManager.START_ABORTED;
 import static android.app.ActivityManager.START_CANCELED;
@@ -51,7 +50,6 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Process.INVALID_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_OPEN;
@@ -59,7 +57,6 @@
 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.RESUMED;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
@@ -70,8 +67,6 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
-import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
-import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
 import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
@@ -95,7 +90,6 @@
 import android.app.WindowConfiguration;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
-import android.content.ComponentName;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -111,15 +105,12 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.voice.IVoiceInteractionSession;
 import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.DebugUtils;
 import android.util.Pools.SynchronizedPool;
 import android.util.Slog;
 import android.window.RemoteTransition;
@@ -256,8 +247,6 @@
 
         /**
          * Generates an {@link ActivityStarter} that is ready to handle a new start request.
-         * @param controller The {@link ActivityStartController} which the starter who will own
-         *                   this instance.
          * @return an {@link ActivityStarter}
          */
         ActivityStarter obtain();
@@ -1028,10 +1017,20 @@
             try {
                 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
                         "shouldAbortBackgroundActivityStart");
-                restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
-                        callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
-                        request.originatingPendingIntent, request.allowBackgroundActivityStart,
-                        intent, checkedOptions);
+                BackgroundActivityStartController balController =
+                        mController.getBackgroundActivityLaunchController();
+                restrictedBgActivity =
+                        balController.shouldAbortBackgroundActivityStart(
+                                callingUid,
+                                callingPid,
+                                callingPackage,
+                                realCallingUid,
+                                realCallingPid,
+                                callerApp,
+                                request.originatingPendingIntent,
+                                request.allowBackgroundActivityStart,
+                                intent,
+                                checkedOptions);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             }
@@ -1261,282 +1260,6 @@
         mController.onExecutionComplete(this);
     }
 
-    private boolean isHomeApp(int uid, @Nullable String packageName) {
-        if (mService.mHomeProcess != null) {
-            // Fast check
-            return uid == mService.mHomeProcess.mUid;
-        }
-        if (packageName == null) {
-            return false;
-        }
-        ComponentName activity =
-                mService.getPackageManagerInternalLocked().getDefaultHomeActivity(
-                        UserHandle.getUserId(uid));
-        return activity != null && packageName.equals(activity.getPackageName());
-    }
-
-    boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
-            final String callingPackage, int realCallingUid, int realCallingPid,
-            WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
-            boolean allowBackgroundActivityStart, Intent intent, ActivityOptions checkedOptions) {
-        // don't abort for the most important UIDs
-        final int callingAppId = UserHandle.getAppId(callingUid);
-        final boolean useCallingUidState =
-                originatingPendingIntent == null || checkedOptions == null
-                        || !checkedOptions.getIgnorePendingIntentCreatorForegroundState();
-        if (useCallingUidState) {
-            if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
-                    || callingAppId == Process.NFC_UID) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG,
-                            "Activity start allowed for important callingUid (" + callingUid + ")");
-                }
-                return false;
-            }
-
-            // Always allow home application to start activities.
-            if (isHomeApp(callingUid, callingPackage)) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG,
-                            "Activity start allowed for home app callingUid (" + callingUid + ")");
-                }
-                return false;
-            }
-
-            // IME should always be allowed to start activity, like IME settings.
-            final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow();
-            if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")");
-                }
-                return false;
-            }
-        }
-
-        // This is used to block background activity launch even if the app is still
-        // visible to user after user clicking home button.
-        final int appSwitchState = mService.getBalAppSwitchesState();
-
-        // don't abort if the callingUid has a visible window or is a persistent system process
-        final int callingUidProcState = mService.mActiveUids.getUidState(callingUid);
-        final boolean callingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid);
-        final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow
-                || callingUidProcState == ActivityManager.PROCESS_STATE_TOP
-                || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
-        final boolean isCallingUidPersistentSystemProcess =
-                callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
-
-        // Normal apps with visible app window will be allowed to start activity if app switching
-        // is allowed, or apps like live wallpaper with non app visible window will be allowed.
-        final boolean appSwitchAllowedOrFg =
-                appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
-        final boolean allowCallingUidStartActivity =
-                ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
-                && callingUidHasAnyVisibleWindow)
-                || isCallingUidPersistentSystemProcess;
-        if (useCallingUidState && allowCallingUidStartActivity) {
-            if (DEBUG_ACTIVITY_STARTS) {
-                Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid
-                        + ", isCallingUidPersistentSystemProcess = "
-                        + isCallingUidPersistentSystemProcess);
-            }
-            return false;
-        }
-        // take realCallingUid into consideration
-        final int realCallingUidProcState = (callingUid == realCallingUid)
-                ? callingUidProcState
-                : mService.mActiveUids.getUidState(realCallingUid);
-        final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
-                ? callingUidHasAnyVisibleWindow
-                : mService.hasActiveVisibleWindow(realCallingUid);
-        final boolean isRealCallingUidForeground = (callingUid == realCallingUid)
-                ? isCallingUidForeground
-                : realCallingUidHasAnyVisibleWindow
-                        || realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP;
-        final int realCallingAppId = UserHandle.getAppId(realCallingUid);
-        final boolean isRealCallingUidPersistentSystemProcess = (callingUid == realCallingUid)
-                ? isCallingUidPersistentSystemProcess
-                : (realCallingAppId == Process.SYSTEM_UID)
-                        || realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
-
-        // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
-        // visible window.
-        if (Process.isSdkSandboxUid(realCallingUid)) {
-            int realCallingSdkSandboxUidToAppUid = Process.getAppUidForSdkSandboxUid(
-                    UserHandle.getAppId(realCallingUid));
-
-            if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "Activity start allowed: uid in SDK sandbox ("
-                            + realCallingUid + ") has visible (non-toast) window.");
-                }
-                return false;
-            }
-        }
-
-        // Legacy behavior allows to use caller foreground state to bypass BAL restriction.
-        final boolean balAllowedByPiSender =
-                PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);
-
-        if (balAllowedByPiSender && realCallingUid != callingUid) {
-            final boolean useCallerPermission =
-                    PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions);
-            if (useCallerPermission && ActivityManager.checkComponentPermission(
-                    android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
-                    realCallingUid, -1, true)
-                    == PackageManager.PERMISSION_GRANTED) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
-                            + ") has BAL permission.");
-                }
-                return false;
-            }
-
-            // don't abort if the realCallingUid has a visible window
-            // TODO(b/171459802): We should check appSwitchAllowed also
-            if (realCallingUidHasAnyVisibleWindow) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
-                            + ") has visible (non-toast) window");
-                }
-                return false;
-            }
-            // if the realCallingUid is a persistent system process, abort if the IntentSender
-            // wasn't allowed to start an activity
-            if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
-                            + ") is persistent system process AND intent sender allowed "
-                            + "(allowBackgroundActivityStart = true)");
-                }
-                return false;
-            }
-            // don't abort if the realCallingUid is an associated companion app
-            if (mService.isAssociatedCompanionApp(UserHandle.getUserId(realCallingUid),
-                    realCallingUid)) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
-                            + ") is companion app");
-                }
-                return false;
-            }
-        }
-        if (useCallingUidState) {
-            // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
-            if (mService.checkPermission(
-                    START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
-                    == PERMISSION_GRANTED) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG,
-                            "Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND "
-                                    + "permission granted for uid "
-                                    + callingUid);
-                }
-                return false;
-            }
-            // don't abort if the caller has the same uid as the recents component
-            if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid
-                            + ") is recents");
-                }
-                return false;
-            }
-            // don't abort if the callingUid is the device owner
-            if (mService.isDeviceOwner(callingUid)) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid
-                            + ") is device owner");
-                }
-                return false;
-            }
-            // don't abort if the callingUid has companion device
-            final int callingUserId = UserHandle.getUserId(callingUid);
-            if (mService.isAssociatedCompanionApp(callingUserId,
-                    callingUid)) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid
-                            + ") is companion app");
-                }
-                return false;
-            }
-            // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
-            if (mService.hasSystemAlertWindowPermission(callingUid,
-                    callingPid, callingPackage)) {
-                Slog.w(TAG, "Background activity start for " + callingPackage
-                        + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
-                return false;
-            }
-        }
-        // If we don't have callerApp at this point, no caller was provided to startActivity().
-        // That's the case for PendingIntent-based starts, since the creator's process might not be
-        // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
-        // caller if caller allows, so that we can make the decision based on its state.
-        int callerAppUid = callingUid;
-        if (callerApp == null && balAllowedByPiSender) {
-            callerApp = mService.getProcessController(realCallingPid, realCallingUid);
-            callerAppUid = realCallingUid;
-        }
-        // don't abort if the callerApp or other processes of that uid are allowed in any way
-        if (callerApp != null && useCallingUidState) {
-            // first check the original calling process
-            if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
-                            + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
-                }
-                return false;
-            }
-            // only if that one wasn't allowed, check the other ones
-            final ArraySet<WindowProcessController> uidProcesses =
-                    mService.mProcessMap.getProcesses(callerAppUid);
-            if (uidProcesses != null) {
-                for (int i = uidProcesses.size() - 1; i >= 0; i--) {
-                    final WindowProcessController proc = uidProcesses.valueAt(i);
-                    if (proc != callerApp
-                            && proc.areBackgroundActivityStartsAllowed(appSwitchState)) {
-                        if (DEBUG_ACTIVITY_STARTS) {
-                            Slog.d(TAG,
-                                    "Background activity start allowed: process " + proc.getPid()
-                                            + " from uid " + callerAppUid + " is allowed");
-                        }
-                        return false;
-                    }
-                }
-            }
-        }
-        // anything that has fallen through would currently be aborted
-        Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
-                + "; callingUid: " + callingUid
-                + "; appSwitchState: " + appSwitchState
-                + "; isCallingUidForeground: " + isCallingUidForeground
-                + "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow
-                + "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,
-                "PROCESS_STATE_", callingUidProcState)
-                + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess
-                + "; realCallingUid: " + realCallingUid
-                + "; isRealCallingUidForeground: " + isRealCallingUidForeground
-                + "; realCallingUidHasAnyVisibleWindow: " + realCallingUidHasAnyVisibleWindow
-                + "; realCallingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,
-                "PROCESS_STATE_", realCallingUidProcState)
-                + "; isRealCallingUidPersistentSystemProcess: "
-                + isRealCallingUidPersistentSystemProcess
-                + "; originatingPendingIntent: " + originatingPendingIntent
-                + "; allowBackgroundActivityStart: " + allowBackgroundActivityStart
-                + "; intent: " + intent
-                + "; callerApp: " + callerApp
-                + "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask())
-                + "]");
-        // log aborted activity start to TRON
-        if (mService.isActivityStartsLoggingEnabled()) {
-            mSupervisor.getActivityMetricsLogger().logAbortedBgActivityStart(intent, callerApp,
-                    callingUid, callingPackage, callingUidProcState, callingUidHasAnyVisibleWindow,
-                    realCallingUid, realCallingUidProcState, realCallingUidHasAnyVisibleWindow,
-                    (originatingPendingIntent != null));
-        }
-        return true;
-    }
-
     /**
      * Creates a launch intent for the given auxiliary resolution data.
      */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index aa15429..d190b4f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2132,10 +2132,19 @@
         if (appThread != null) {
             callerApp = getProcessController(appThread);
         }
-        final ActivityStarter starter = getActivityStartController().obtainStarter(
-                null /* intent */, "moveTaskToFront");
-        if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1,
-                -1, callerApp, null, false, null, null)) {
+        final BackgroundActivityStartController balController =
+                getActivityStartController().getBackgroundActivityLaunchController();
+        if (balController.shouldAbortBackgroundActivityStart(
+                callingUid,
+                callingPid,
+                callingPackage,
+                -1,
+                -1,
+                callerApp,
+                null,
+                false,
+                null,
+                null)) {
             if (!isBackgroundActivityStartsEnabled()) {
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index e80c260..fd6c974 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -119,10 +119,20 @@
                 if (appThread != null) {
                     callerApp = mService.getProcessController(appThread);
                 }
-                final ActivityStarter starter = mService.getActivityStartController().obtainStarter(
-                        null /* intent */, "moveToFront");
-                if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid,
-                        callingPackage, -1, -1, callerApp, null, false, null, null)) {
+                final BackgroundActivityStartController balController =
+                        mService.getActivityStartController()
+                                .getBackgroundActivityLaunchController();
+                if (balController.shouldAbortBackgroundActivityStart(
+                        callingUid,
+                        callingPid,
+                        callingPackage,
+                        -1,
+                        -1,
+                        callerApp,
+                        null,
+                        false,
+                        null,
+                        null)) {
                     if (!mService.isBackgroundActivityStartsEnabled()) {
                         return;
                     }
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
new file mode 100644
index 0000000..d515a27
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.Slog;
+
+import com.android.server.am.PendingIntentRecord;
+
+/**
+ * Helper class to check permissions for starting Activities.
+ *
+ * <p>This class collects all the logic to prevent malicious attempts to start activities.
+ */
+public class BackgroundActivityStartController {
+
+    private static final String TAG =
+            TAG_WITH_CLASS_NAME ? "BackgroundActivityStartController" : TAG_ATM;
+
+    private final ActivityTaskManagerService mService;
+    private final ActivityTaskSupervisor mSupervisor;
+
+    BackgroundActivityStartController(
+            final ActivityTaskManagerService service, final ActivityTaskSupervisor supervisor) {
+        mService = service;
+        mSupervisor = supervisor;
+    }
+
+    private boolean isHomeApp(int uid, @Nullable String packageName) {
+        if (mService.mHomeProcess != null) {
+            // Fast check
+            return uid == mService.mHomeProcess.mUid;
+        }
+        if (packageName == null) {
+            return false;
+        }
+        ComponentName activity =
+                mService.getPackageManagerInternalLocked()
+                        .getDefaultHomeActivity(UserHandle.getUserId(uid));
+        return activity != null && packageName.equals(activity.getPackageName());
+    }
+
+    boolean shouldAbortBackgroundActivityStart(
+            int callingUid,
+            int callingPid,
+            final String callingPackage,
+            int realCallingUid,
+            int realCallingPid,
+            WindowProcessController callerApp,
+            PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart,
+            Intent intent,
+            ActivityOptions checkedOptions) {
+        // don't abort for the most important UIDs
+        final int callingAppId = UserHandle.getAppId(callingUid);
+        final boolean useCallingUidState =
+                originatingPendingIntent == null
+                        || checkedOptions == null
+                        || !checkedOptions.getIgnorePendingIntentCreatorForegroundState();
+        if (useCallingUidState) {
+            if (callingUid == Process.ROOT_UID
+                    || callingAppId == Process.SYSTEM_UID
+                    || callingAppId == Process.NFC_UID) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Activity start allowed for important callingUid (" + callingUid + ")");
+                }
+                return false;
+            }
+
+            // Always allow home application to start activities.
+            if (isHomeApp(callingUid, callingPackage)) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Activity start allowed for home app callingUid (" + callingUid + ")");
+                }
+                return false;
+            }
+
+            // IME should always be allowed to start activity, like IME settings.
+            final WindowState imeWindow =
+                    mService.mRootWindowContainer.getCurrentInputMethodWindow();
+            if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")");
+                }
+                return false;
+            }
+        }
+
+        // This is used to block background activity launch even if the app is still
+        // visible to user after user clicking home button.
+        final int appSwitchState = mService.getBalAppSwitchesState();
+
+        // don't abort if the callingUid has a visible window or is a persistent system process
+        final int callingUidProcState = mService.mActiveUids.getUidState(callingUid);
+        final boolean callingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid);
+        final boolean isCallingUidForeground =
+                callingUidHasAnyVisibleWindow
+                        || callingUidProcState == ActivityManager.PROCESS_STATE_TOP
+                        || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
+        final boolean isCallingUidPersistentSystemProcess =
+                callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+
+        // Normal apps with visible app window will be allowed to start activity if app switching
+        // is allowed, or apps like live wallpaper with non app visible window will be allowed.
+        final boolean appSwitchAllowedOrFg =
+                appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
+        final boolean allowCallingUidStartActivity =
+                ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
+                                && callingUidHasAnyVisibleWindow)
+                        || isCallingUidPersistentSystemProcess;
+        if (useCallingUidState && allowCallingUidStartActivity) {
+            if (DEBUG_ACTIVITY_STARTS) {
+                Slog.d(
+                        TAG,
+                        "Activity start allowed: callingUidHasAnyVisibleWindow = "
+                                + callingUid
+                                + ", isCallingUidPersistentSystemProcess = "
+                                + isCallingUidPersistentSystemProcess);
+            }
+            return false;
+        }
+        // take realCallingUid into consideration
+        final int realCallingUidProcState =
+                (callingUid == realCallingUid)
+                        ? callingUidProcState
+                        : mService.mActiveUids.getUidState(realCallingUid);
+        final boolean realCallingUidHasAnyVisibleWindow =
+                (callingUid == realCallingUid)
+                        ? callingUidHasAnyVisibleWindow
+                        : mService.hasActiveVisibleWindow(realCallingUid);
+        final boolean isRealCallingUidForeground =
+                (callingUid == realCallingUid)
+                        ? isCallingUidForeground
+                        : realCallingUidHasAnyVisibleWindow
+                                || realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP;
+        final int realCallingAppId = UserHandle.getAppId(realCallingUid);
+        final boolean isRealCallingUidPersistentSystemProcess =
+                (callingUid == realCallingUid)
+                        ? isCallingUidPersistentSystemProcess
+                        : (realCallingAppId == Process.SYSTEM_UID)
+                                || realCallingUidProcState
+                                        <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+
+        // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
+        // visible window.
+        if (Process.isSdkSandboxUid(realCallingUid)) {
+            int realCallingSdkSandboxUidToAppUid =
+                    Process.getAppUidForSdkSandboxUid(UserHandle.getAppId(realCallingUid));
+
+            if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Activity start allowed: uid in SDK sandbox ("
+                                    + realCallingUid
+                                    + ") has visible (non-toast) window.");
+                }
+                return false;
+            }
+        }
+
+        // Legacy behavior allows to use caller foreground state to bypass BAL restriction.
+        final boolean balAllowedByPiSender =
+                PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);
+
+        if (balAllowedByPiSender && realCallingUid != callingUid) {
+            final boolean useCallerPermission =
+                    PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions);
+            if (useCallerPermission
+                    && ActivityManager.checkComponentPermission(
+                                    android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
+                                    realCallingUid,
+                                    -1,
+                                    true)
+                            == PackageManager.PERMISSION_GRANTED) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Activity start allowed: realCallingUid ("
+                                    + realCallingUid
+                                    + ") has BAL permission.");
+                }
+                return false;
+            }
+
+            // don't abort if the realCallingUid has a visible window
+            // TODO(b/171459802): We should check appSwitchAllowed also
+            if (realCallingUidHasAnyVisibleWindow) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Activity start allowed: realCallingUid ("
+                                    + realCallingUid
+                                    + ") has visible (non-toast) window");
+                }
+                return false;
+            }
+            // if the realCallingUid is a persistent system process, abort if the IntentSender
+            // wasn't allowed to start an activity
+            if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Activity start allowed: realCallingUid ("
+                                    + realCallingUid
+                                    + ") is persistent system process AND intent sender allowed "
+                                    + "(allowBackgroundActivityStart = true)");
+                }
+                return false;
+            }
+            // don't abort if the realCallingUid is an associated companion app
+            if (mService.isAssociatedCompanionApp(
+                    UserHandle.getUserId(realCallingUid), realCallingUid)) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Activity start allowed: realCallingUid ("
+                                    + realCallingUid
+                                    + ") is companion app");
+                }
+                return false;
+            }
+        }
+        if (useCallingUidState) {
+            // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
+            if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
+                    == PERMISSION_GRANTED) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND "
+                                    + "permission granted for uid "
+                                    + callingUid);
+                }
+                return false;
+            }
+            // don't abort if the caller has the same uid as the recents component
+            if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Background activity start allowed: callingUid ("
+                                    + callingUid
+                                    + ") is recents");
+                }
+                return false;
+            }
+            // don't abort if the callingUid is the device owner
+            if (mService.isDeviceOwner(callingUid)) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Background activity start allowed: callingUid ("
+                                    + callingUid
+                                    + ") is device owner");
+                }
+                return false;
+            }
+            // don't abort if the callingUid has companion device
+            final int callingUserId = UserHandle.getUserId(callingUid);
+            if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Background activity start allowed: callingUid ("
+                                    + callingUid
+                                    + ") is companion app");
+                }
+                return false;
+            }
+            // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
+            if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
+                Slog.w(
+                        TAG,
+                        "Background activity start for "
+                                + callingPackage
+                                + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+                return false;
+            }
+        }
+        // If we don't have callerApp at this point, no caller was provided to startActivity().
+        // That's the case for PendingIntent-based starts, since the creator's process might not be
+        // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
+        // caller if caller allows, so that we can make the decision based on its state.
+        int callerAppUid = callingUid;
+        if (callerApp == null && balAllowedByPiSender) {
+            callerApp = mService.getProcessController(realCallingPid, realCallingUid);
+            callerAppUid = realCallingUid;
+        }
+        // don't abort if the callerApp or other processes of that uid are allowed in any way
+        if (callerApp != null && useCallingUidState) {
+            // first check the original calling process
+            if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {
+                if (DEBUG_ACTIVITY_STARTS) {
+                    Slog.d(
+                            TAG,
+                            "Background activity start allowed: callerApp process (pid = "
+                                    + callerApp.getPid()
+                                    + ", uid = "
+                                    + callerAppUid
+                                    + ") is allowed");
+                }
+                return false;
+            }
+            // only if that one wasn't allowed, check the other ones
+            final ArraySet<WindowProcessController> uidProcesses =
+                    mService.mProcessMap.getProcesses(callerAppUid);
+            if (uidProcesses != null) {
+                for (int i = uidProcesses.size() - 1; i >= 0; i--) {
+                    final WindowProcessController proc = uidProcesses.valueAt(i);
+                    if (proc != callerApp
+                            && proc.areBackgroundActivityStartsAllowed(appSwitchState)) {
+                        if (DEBUG_ACTIVITY_STARTS) {
+                            Slog.d(
+                                    TAG,
+                                    "Background activity start allowed: process "
+                                            + proc.getPid()
+                                            + " from uid "
+                                            + callerAppUid
+                                            + " is allowed");
+                        }
+                        return false;
+                    }
+                }
+            }
+        }
+        // anything that has fallen through would currently be aborted
+        Slog.w(
+                TAG,
+                "Background activity start [callingPackage: "
+                        + callingPackage
+                        + "; callingUid: "
+                        + callingUid
+                        + "; appSwitchState: "
+                        + appSwitchState
+                        + "; isCallingUidForeground: "
+                        + isCallingUidForeground
+                        + "; callingUidHasAnyVisibleWindow: "
+                        + callingUidHasAnyVisibleWindow
+                        + "; callingUidProcState: "
+                        + DebugUtils.valueToString(
+                                ActivityManager.class, "PROCESS_STATE_", callingUidProcState)
+                        + "; isCallingUidPersistentSystemProcess: "
+                        + isCallingUidPersistentSystemProcess
+                        + "; realCallingUid: "
+                        + realCallingUid
+                        + "; isRealCallingUidForeground: "
+                        + isRealCallingUidForeground
+                        + "; realCallingUidHasAnyVisibleWindow: "
+                        + realCallingUidHasAnyVisibleWindow
+                        + "; realCallingUidProcState: "
+                        + DebugUtils.valueToString(
+                                ActivityManager.class, "PROCESS_STATE_", realCallingUidProcState)
+                        + "; isRealCallingUidPersistentSystemProcess: "
+                        + isRealCallingUidPersistentSystemProcess
+                        + "; originatingPendingIntent: "
+                        + originatingPendingIntent
+                        + "; allowBackgroundActivityStart: "
+                        + allowBackgroundActivityStart
+                        + "; intent: "
+                        + intent
+                        + "; callerApp: "
+                        + callerApp
+                        + "; inVisibleTask: "
+                        + (callerApp != null && callerApp.hasActivityInVisibleTask())
+                        + "]");
+        // log aborted activity start to TRON
+        if (mService.isActivityStartsLoggingEnabled()) {
+            mSupervisor
+                    .getActivityMetricsLogger()
+                    .logAbortedBgActivityStart(
+                            intent,
+                            callerApp,
+                            callingUid,
+                            callingPackage,
+                            callingUidProcState,
+                            callingUidHasAnyVisibleWindow,
+                            realCallingUid,
+                            realCallingUidProcState,
+                            realCallingUidHasAnyVisibleWindow,
+                            (originatingPendingIntent != null));
+        }
+        return true;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 6602d29..4f506a5 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -14,3 +14,6 @@
 tigerhuang@google.com
 lihongyu@google.com
 mariiasand@google.com
+
+per-file BackgroundActivityStartController.java = set noparent
+per-file BackgroundActivityStartController.java = brufino@google.com, ogunwale@google.com, louischang@google.com, lus@google.com, rickywai@google.com
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 4ca14dd..c8782e5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -147,6 +147,9 @@
     @Before
     public void setUp() throws Exception {
         mController = mock(ActivityStartController.class);
+        BackgroundActivityStartController balController =
+                new BackgroundActivityStartController(mAtm, mSupervisor);
+        doReturn(balController).when(mController).getBackgroundActivityLaunchController();
         mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
         clearInvocations(mActivityMetricsLogger);
     }
@@ -208,10 +211,13 @@
             int expectedResult) {
         final ActivityTaskManagerService service = mAtm;
         final IPackageManager packageManager = mock(IPackageManager.class);
-        final ActivityStartController controller = mock(ActivityStartController.class);
 
-        final ActivityStarter starter = new ActivityStarter(controller, service,
-                service.mTaskSupervisor, mock(ActivityStartInterceptor.class));
+        final ActivityStarter starter =
+                new ActivityStarter(
+                        mController,
+                        service,
+                        service.mTaskSupervisor,
+                        mock(ActivityStartInterceptor.class));
         prepareStarter(launchFlags);
         final IApplicationThread caller = mock(IApplicationThread.class);
         final WindowProcessListener listener = mock(WindowProcessListener.class);