BG-FGS-launch restriction exemptions.

1. Add a new permission START_FOREGROUND_SERVICES_FROM_BACKGROUND.
2. Add a new bind flag BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND
3. BroadcastOptions.setTemporaryAppWhitelistDuration() is currently
protected with CHANGE_DEVICE_IDLE_TEMP_WHITELIST permission, also open
it for START_ACTIVITIES_FROM_BACKGROUND and
START_FOREGROUND_SERVICES_FROM_BACKGROUND permission.
4. Exempt SYSTEM_ALERT_WINDOW permission.
5. if Context.startForegroundService() or Service.startForeground() is
restricted by BG-FGS-launch restriction, and app's targetSdkVersion is S
and above, throw a IllegalStateException.

Bug: 171305836
Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testFgsBindingFlagFGS
atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testFgsBindingFlagActivity
atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testFgsStartSystemAlertWindow
atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testFgsStartFromBGException

Change-Id: Iff3ed65e174a8406d4d6045cda42bdde6cecf30d
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 505d246..7d82f43 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -228,6 +228,7 @@
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
     field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
+    field public static final String START_FOREGROUND_SERVICES_FROM_BACKGROUND = "android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND";
     field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
@@ -603,7 +604,7 @@
     method public static android.app.BroadcastOptions makeBasic();
     method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
     method public void setDontSendToRestrictedApps(boolean);
-    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long);
+    method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long);
     method public android.os.Bundle toBundle();
   }
 
@@ -1764,6 +1765,7 @@
     field public static final String BACKUP_SERVICE = "backup";
     field public static final String BATTERY_STATS_SERVICE = "batterystats";
     field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
+    field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 03dca30..298c455 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -90,7 +90,9 @@
      * power allowlist when this broadcast is being delivered to it.
      * @param duration The duration in milliseconds; 0 means to not place on allowlist.
      */
-    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
+            android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
+            android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
     public void setTemporaryAppWhitelistDuration(long duration) {
         mTemporaryAppWhitelistDuration = duration;
     }
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index b96b54a..3798de9 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -697,6 +697,10 @@
      * service element of manifest file. The value of attribute
      * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p>
      *
+     * @throws IllegalStateException If the app targeting API is
+     * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from
+     * becoming foreground service due to background restriction.
+     *
      * @param id The identifier for this notification as per
      * {@link NotificationManager#notify(int, Notification)
      * NotificationManager.notify(int, Notification)}; must not be 0.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6a3f6b4..8f92bf1 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -367,6 +367,16 @@
     /***********    Hidden flags below this line ***********/
 
     /**
+     * Flag for {@link #bindService}: allow background foreground service starts from the bound
+     * service's process.
+     * This flag is only respected if the caller is holding
+     * {@link android.Manifest.permission#START_FOREGROUND_SERVICES_FROM_BACKGROUND}.
+     * @hide
+     */
+    @SystemApi
+    public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 0x00040000;
+
+    /**
      * Flag for {@link #bindService}: This flag is intended to be used only by the system to adjust
      * the scheduling policy for IMEs (and any other out-of-process user-visible components that
      * work closely with the top app) so that UI hosted in such services can have the same
@@ -3107,6 +3117,10 @@
      * @throws SecurityException If the caller does not have permission to access the service
      * or the service can not be found.
      *
+     * @throws IllegalStateException If the caller app's targeting API is
+     * {@link android.os.Build.VERSION_CODES#S} or later, and the foreground service is restricted
+     * from start due to background restriction.
+     *
      * @see #stopService
      * @see android.app.Service#startForeground(int, android.app.Notification)
      */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ea667277..c9e8f71 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2557,6 +2557,10 @@
     <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
         android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" />
 
+    <!-- @SystemApi @hide Allows an application to start foreground services from background -->
+    <permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"
+                android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" />
+
     <!-- @SystemApi Must be required by activities that handle the intent action
          {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that
          hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system.
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a97af4b..2a699ea 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -146,6 +146,7 @@
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
     <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
     <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
+    <uses-permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
     <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java
index 43474d5..61ccf11 100644
--- a/services/core/java/com/android/server/am/ActiveInstrumentation.java
+++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java
@@ -52,6 +52,9 @@
     // Whether the caller holds START_ACTIVITIES_FROM_BACKGROUND permission
     boolean mHasBackgroundActivityStartsPermission;
 
+    // Whether the caller holds START_FOREGROUND_SERVICES_FROM_BACKGROUND permission
+    boolean mHasBackgroundForegroundServiceStartsPermission;
+
     // As given to us
     Bundle mArguments;
 
@@ -128,6 +131,8 @@
         }
         pw.print("mHasBackgroundActivityStartsPermission=");
         pw.println(mHasBackgroundActivityStartsPermission);
+        pw.print("mHasBackgroundForegroundServiceStartsPermission=");
+        pw.println(mHasBackgroundForegroundServiceStartsPermission);
         pw.print(prefix); pw.print("mArguments=");
         pw.println(mArguments);
     }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d6f7299..8729026 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -17,6 +17,8 @@
 package com.android.server.am;
 
 import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
+import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
+import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
@@ -56,9 +58,11 @@
 import android.app.Service;
 import android.app.ServiceStartArgs;
 import android.app.admin.DevicePolicyEventLogger;
+import android.app.compat.CompatChanges;
 import android.appwidget.AppWidgetManagerInternal;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledSince;
 import android.content.ComponentName;
 import android.content.ComponentName.WithComponentName;
 import android.content.Context;
@@ -104,7 +108,6 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.ServiceState;
-import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BatteryStatsImpl;
@@ -147,30 +150,40 @@
 
     public static final int FGS_FEATURE_DENIED = 0;
     public static final int FGS_FEATURE_ALLOWED_BY_UID_STATE = 1;
-    public static final int FGS_FEATURE_ALLOWED_BY_UID_VISIBLE = 2;
-    public static final int FGS_FEATURE_ALLOWED_BY_FLAG = 3;
-    public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_UID = 4;
-    public static final int FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION = 5;
-    public static final int FGS_FEATURE_ALLOWED_BY_TOKEN = 6;
-    public static final int FGS_FEATURE_ALLOWED_BY_PERMISSION = 7;
-    public static final int FGS_FEATURE_ALLOWED_BY_WHITELIST = 8;
-    public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER = 9;
-    public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 10;
-    public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 11;
+    public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 2;
+    public static final int FGS_FEATURE_ALLOWED_BY_UID_VISIBLE = 3;
+    public static final int FGS_FEATURE_ALLOWED_BY_FLAG = 4;
+    public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_UID = 5;
+    public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 6;
+    public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION = 7;
+    public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN = 8;
+    public static final int FGS_FEATURE_ALLOWED_BY_FGS_TOKEN = 9;
+    public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION = 10;
+    public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION = 12;
+    public static final int FGS_FEATURE_ALLOWED_BY_ALLOWLIST = 13;
+    public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER = 14;
+    public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 15;
+    public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION = 16;
+    public static final int FGS_FEATURE_ALLOWED_BY_FGS_BINDING = 17;
 
     @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = {
             FGS_FEATURE_DENIED,
             FGS_FEATURE_ALLOWED_BY_UID_STATE,
+            FGS_FEATURE_ALLOWED_BY_PROC_STATE,
             FGS_FEATURE_ALLOWED_BY_UID_VISIBLE,
             FGS_FEATURE_ALLOWED_BY_FLAG,
             FGS_FEATURE_ALLOWED_BY_SYSTEM_UID,
-            FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION,
-            FGS_FEATURE_ALLOWED_BY_TOKEN,
-            FGS_FEATURE_ALLOWED_BY_PERMISSION,
-            FGS_FEATURE_ALLOWED_BY_WHITELIST,
+            FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION,
+            FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION,
+            FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN,
+            FGS_FEATURE_ALLOWED_BY_FGS_TOKEN,
+            FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION,
+            FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION,
+            FGS_FEATURE_ALLOWED_BY_ALLOWLIST,
             FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER,
-            FGS_FEATURE_ALLOWED_BY_PROC_STATE,
-            FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST
+            FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST,
+            FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION,
+            FGS_FEATURE_ALLOWED_BY_FGS_BINDING
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface FgsFeatureRetCode {}
@@ -242,14 +255,12 @@
     AppWidgetManagerInternal mAppWidgetManagerInternal;
 
     // white listed packageName.
-    ArraySet<String> mWhiteListAllowWhileInUsePermissionInFgs = new ArraySet<>();
+    ArraySet<String> mAllowListWhileInUsePermissionInFgs = new ArraySet<>();
 
     // TODO: remove this after feature development is done
     private static final SimpleDateFormat DATE_FORMATTER =
             new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
-    private final IPlatformCompat mPlatformCompat;
-
     /**
      * The BG-launch FGS restriction feature is going to be allowed only for apps targetSdkVersion
      * is higher than R.
@@ -258,6 +269,14 @@
     @Disabled
     static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L;
 
+    /**
+     * If a service can not become foreground service due to BG-FGS-launch restriction or other
+     * reasons, throws an IllegalStateException.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
+    static final long FGS_START_EXCEPTION_CHANGE_ID = 174041399L;
+
     final Runnable mLastAnrDumpClearer = new Runnable() {
         @Override public void run() {
             synchronized (mAm) {
@@ -456,26 +475,25 @@
                 ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8;
 
         final IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
-        mPlatformCompat = IPlatformCompat.Stub.asInterface(b);
     }
 
     void systemServicesReady() {
         AppStateTracker ast = LocalServices.getService(AppStateTracker.class);
         ast.addServiceStateListener(new ForcedStandbyListener());
         mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class);
-        setWhiteListAllowWhileInUsePermissionInFgs();
+        setAllowListWhileInUsePermissionInFgs();
     }
 
-    private void setWhiteListAllowWhileInUsePermissionInFgs() {
+    private void setAllowListWhileInUsePermissionInFgs() {
         final String attentionServicePackageName =
                 mAm.mContext.getPackageManager().getAttentionServicePackageName();
         if (!TextUtils.isEmpty(attentionServicePackageName)) {
-            mWhiteListAllowWhileInUsePermissionInFgs.add(attentionServicePackageName);
+            mAllowListWhileInUsePermissionInFgs.add(attentionServicePackageName);
         }
         final String systemCaptionsServicePackageName =
                 mAm.mContext.getPackageManager().getSystemCaptionsServicePackageName();
         if (!TextUtils.isEmpty(systemCaptionsServicePackageName)) {
-            mWhiteListAllowWhileInUsePermissionInFgs.add(systemCaptionsServicePackageName);
+            mAllowListWhileInUsePermissionInFgs.add(systemCaptionsServicePackageName);
         }
     }
 
@@ -583,13 +601,27 @@
                     Slog.wtf(TAG, "Background started FGS " + r.mInfoAllowStartForeground);
                     r.mLoggedInfoAllowStartForeground = true;
                 }
-                if (r.mAllowStartForeground == FGS_FEATURE_DENIED
-                        && (mAm.mConstants.mFlagFgsStartRestrictionEnabled
-                        || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) {
-                    Slog.w(TAG, "startForegroundService() not allowed due to "
+                if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) {
+                    String msg = "startForegroundService() not allowed due to "
                             + "mAllowStartForeground false: service "
-                            + r.shortInstanceName);
+                            + r.shortInstanceName;
+                    Slog.w(TAG, msg);
                     showFgsBgRestrictedNotificationLocked(r);
+                    ApplicationInfo aInfo = null;
+                    try {
+                        aInfo = AppGlobals.getPackageManager().getApplicationInfo(
+                                callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
+                                userId);
+                    } catch (android.os.RemoteException e) {
+                        // pm is in same process, this will never happen.
+                    }
+                    if (aInfo == null) {
+                        throw new SecurityException("startServiceLocked failed, "
+                                + "could not resolve client package " + callingPackage);
+                    }
+                    if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
+                        throw new IllegalStateException(msg);
+                    }
                     return null;
                 }
             }
@@ -1449,7 +1481,7 @@
             }
 
             try {
-                boolean ignoreForeground = false;
+                String ignoreForeground = null;
                 final int mode = mAm.getAppOpsManager().checkOpNoThrow(
                         AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
                 switch (mode) {
@@ -1459,9 +1491,9 @@
                         break;
                     case AppOpsManager.MODE_IGNORED:
                         // Whoops, silently ignore this.
-                        Slog.w(TAG, "Service.startForeground() not allowed due to app op: service "
-                                + r.shortInstanceName);
-                        ignoreForeground = true;
+                        ignoreForeground = "Service.startForeground() not allowed due to app op: "
+                                + "service " + r.shortInstanceName;
+                        Slog.w(TAG, ignoreForeground);
                         break;
                     default:
                         throw new SecurityException("Foreground not allowed as per app op");
@@ -1469,19 +1501,18 @@
 
                 // Apps that are TOP or effectively similar may call startForeground() on
                 // their services even if they are restricted from doing that while in bg.
-                if (!ignoreForeground
+                if (ignoreForeground == null
                         && !appIsTopLocked(r.appInfo.uid)
                         && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
-                    Slog.w(TAG,
-                            "Service.startForeground() not allowed due to bg restriction: service "
-                            + r.shortInstanceName);
+                    ignoreForeground = "Service.startForeground() not allowed due to bg restriction"
+                            + ":service " + r.shortInstanceName;
+                    Slog.w(TAG, ignoreForeground);
                     // Back off of any foreground expectations around this service, since we've
                     // just turned down its fg request.
                     updateServiceForegroundLocked(r.app, false);
-                    ignoreForeground = true;
                 }
 
-                if (!ignoreForeground) {
+                if (ignoreForeground == null) {
                     if (isFgsBgStart(r.mAllowStartForeground)) {
                         if (!r.mLoggedInfoAllowStartForeground) {
                             Slog.wtf(TAG, "Background started FGS "
@@ -1489,14 +1520,13 @@
                             r.mLoggedInfoAllowStartForeground = true;
                         }
                         if (r.mAllowStartForeground == FGS_FEATURE_DENIED
-                                && (mAm.mConstants.mFlagFgsStartRestrictionEnabled
-                                || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) {
-                            Slog.w(TAG, "Service.startForeground() not allowed due to "
-                                            + "mAllowStartForeground false: service "
-                                            + r.shortInstanceName);
+                                && isBgFgsRestrictionEnabled(r)) {
+                            ignoreForeground = "Service.startForeground() not allowed due to "
+                                    + "mAllowStartForeground false: service "
+                                    + r.shortInstanceName;
+                            Slog.w(TAG, ignoreForeground);
                             showFgsBgRestrictedNotificationLocked(r);
                             updateServiceForegroundLocked(r.app, true);
-                            ignoreForeground = true;
                         }
                     }
                 }
@@ -1505,7 +1535,7 @@
                 // services, so now that we've enforced the startForegroundService() contract
                 // we only do the machinery of making the service foreground when the app
                 // is not restricted.
-                if (!ignoreForeground) {
+                if (ignoreForeground == null) {
                     if (r.foregroundId != id) {
                         cancelForegroundNotificationLocked(r);
                         r.foregroundId = id;
@@ -1567,6 +1597,10 @@
                     if (DEBUG_FOREGROUND_SERVICE) {
                         Slog.d(TAG, "Suppressing startForeground() for FAS " + r);
                     }
+                    if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, r.appInfo.uid)
+                            && isBgFgsRestrictionEnabled(r)) {
+                        throw new IllegalStateException(ignoreForeground);
+                    }
                 }
             } finally {
                 if (stopProcStatsOp) {
@@ -2094,6 +2128,12 @@
                     "BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS");
         }
 
+        if ((flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) {
+            mAm.enforceCallingPermission(
+                    android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND,
+                    "BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND");
+        }
+
         final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
         final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
         final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
@@ -2239,6 +2279,11 @@
             if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
                 s.setAllowedBgActivityStartsByBinding(true);
             }
+
+            if ((flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) {
+                s.setAllowedBgFgsStartsByBinding(true);
+            }
+
             if (s.app != null) {
                 updateServiceClientActivitiesLocked(s.app, c, true);
             }
@@ -2256,7 +2301,6 @@
                     return 0;
                 }
             }
-
             setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, false);
 
             if (s.app != null) {
@@ -3647,6 +3691,9 @@
             if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
                 s.updateIsAllowedBgActivityStartsByBinding();
             }
+            if ((c.flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) {
+                s.updateIsAllowedBgFgsStartsByBinding();
+            }
             if (s.app != null) {
                 updateServiceClientActivitiesLocked(s.app, c, true);
             }
@@ -5113,13 +5160,22 @@
         }
 
         if (ret == FGS_FEATURE_DENIED) {
+            for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+                final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
+                if (pr.uid == callingUid) {
+                    if (pr.areBackgroundActivityStartsAllowedByToken()) {
+                        ret = FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (ret == FGS_FEATURE_DENIED) {
             if (r.app != null) {
                 ActiveInstrumentation instr = r.app.getActiveInstrumentation();
                 if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
-                    ret = FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION;
-                }
-                if (r.app.areBackgroundActivityStartsAllowedByToken()) {
-                    ret = FGS_FEATURE_ALLOWED_BY_TOKEN;
+                    ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
                 }
             }
         }
@@ -5127,15 +5183,15 @@
         if (ret == FGS_FEATURE_DENIED) {
             if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
                     == PERMISSION_GRANTED) {
-                ret = FGS_FEATURE_ALLOWED_BY_PERMISSION;
+                ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION;
             }
         }
 
         if (ret == FGS_FEATURE_DENIED) {
-            final boolean isWhiteListedPackage =
-                    mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage);
-            if (isWhiteListedPackage) {
-                ret = FGS_FEATURE_ALLOWED_BY_WHITELIST;
+            final boolean isAllowedPackage =
+                    mAllowListWhileInUsePermissionInFgs.contains(callingPackage);
+            if (isAllowedPackage) {
+                ret = FGS_FEATURE_ALLOWED_BY_ALLOWLIST;
             }
         }
 
@@ -5187,6 +5243,39 @@
         }
 
         if (ret == FGS_FEATURE_DENIED) {
+            for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+                final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
+                if (pr.uid == callingUid) {
+                    if (pr.areBackgroundFgsStartsAllowedByToken()) {
+                        ret = FGS_FEATURE_ALLOWED_BY_FGS_BINDING;
+                        break;
+                    } else {
+                        final ActiveInstrumentation instr = pr.getActiveInstrumentation();
+                        if (instr != null
+                                && instr.mHasBackgroundForegroundServiceStartsPermission) {
+                            ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (ret == FGS_FEATURE_DENIED) {
+            if (mAm.checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid,
+                    callingUid) == PERMISSION_GRANTED) {
+                ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION;
+            }
+        }
+
+        if (ret == FGS_FEATURE_DENIED) {
+            if (mAm.checkPermission(SYSTEM_ALERT_WINDOW, callingPid,
+                    callingUid) == PERMISSION_GRANTED) {
+                ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION;
+            }
+        }
+
+        if (ret == FGS_FEATURE_DENIED) {
             if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
                     && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
                 // uid is on DeviceIdleController's allowlist.
@@ -5217,26 +5306,36 @@
                 return "DENIED";
             case FGS_FEATURE_ALLOWED_BY_UID_STATE:
                 return "ALLOWED_BY_UID_STATE";
+            case FGS_FEATURE_ALLOWED_BY_PROC_STATE:
+                return "ALLOWED_BY_PROC_STATE";
             case FGS_FEATURE_ALLOWED_BY_UID_VISIBLE:
                 return "ALLOWED_BY_UID_VISIBLE";
             case FGS_FEATURE_ALLOWED_BY_FLAG:
                 return "ALLOWED_BY_FLAG";
             case FGS_FEATURE_ALLOWED_BY_SYSTEM_UID:
                 return "ALLOWED_BY_SYSTEM_UID";
-            case FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION:
-                return "ALLOWED_BY_INSTR_PERMISSION";
-            case FGS_FEATURE_ALLOWED_BY_TOKEN:
-                return "ALLOWED_BY_TOKEN";
-            case FGS_FEATURE_ALLOWED_BY_PERMISSION:
-                return "ALLOWED_BY_PERMISSION";
-            case FGS_FEATURE_ALLOWED_BY_WHITELIST:
+            case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
+                return "ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION";
+            case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION:
+                return "ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION";
+            case FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN:
+                return "ALLOWED_BY_ACTIVITY_TOKEN";
+            case FGS_FEATURE_ALLOWED_BY_FGS_TOKEN:
+                return "ALLOWED_BY_FGS_TOKEN";
+            case FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION:
+                return "ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION";
+            case FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION:
+                return "ALLOWED_BY_BACKGROUND_FGS_PERMISSION";
+            case FGS_FEATURE_ALLOWED_BY_ALLOWLIST:
                 return "ALLOWED_BY_WHITELIST";
             case FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER:
                 return "ALLOWED_BY_DEVICE_OWNER";
-            case FGS_FEATURE_ALLOWED_BY_PROC_STATE:
-                return "ALLOWED_BY_PROC_STATE";
             case FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST:
                 return "ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST";
+            case FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION:
+                return "ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION";
+            case FGS_FEATURE_ALLOWED_BY_FGS_BINDING:
+                return "ALLOWED_BY_FGS_BINDING";
             default:
                 return "";
         }
@@ -5271,11 +5370,10 @@
                 NOTE_FOREGROUND_SERVICE_BG_LAUNCH, n.build(), UserHandle.ALL);
     }
 
-    private boolean isChangeEnabled(long changeId, ServiceRecord r) {
-        boolean enabled = false;
-        try {
-            enabled = mPlatformCompat.isChangeEnabled(changeId, r.appInfo);
-        } catch (RemoteException e) { }
-        return enabled;
+    private boolean isBgFgsRestrictionEnabled(ServiceRecord r) {
+        if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
+            return true;
+        }
+        return CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9eca15b..75e8b13 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22,6 +22,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
+import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
@@ -13321,15 +13322,22 @@
                 // See if the caller is allowed to do this.  Note we are checking against
                 // the actual real caller (not whoever provided the operation as say a
                 // PendingIntent), because that who is actually supplied the arguments.
-                if (checkComponentPermission(
-                        android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
+                if (checkComponentPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
+                        realCallingPid, realCallingUid, -1, true)
+                        != PackageManager.PERMISSION_GRANTED
+                        && checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND,
+                        realCallingPid, realCallingUid, -1, true)
+                        != PackageManager.PERMISSION_GRANTED
+                        && checkComponentPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND,
                         realCallingPid, realCallingUid, -1, true)
                         != PackageManager.PERMISSION_GRANTED) {
                     String msg = "Permission Denial: " + intent.getAction()
                             + " broadcast from " + callerPackage + " (pid=" + callingPid
                             + ", uid=" + callingUid + ")"
                             + " requires "
-                            + android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
+                            + CHANGE_DEVICE_IDLE_TEMP_WHITELIST + " or "
+                            + START_ACTIVITIES_FROM_BACKGROUND + " or "
+                            + START_FOREGROUND_SERVICES_FROM_BACKGROUND;
                     Slog.w(TAG, msg);
                     throw new SecurityException(msg);
                 }
@@ -14289,8 +14297,10 @@
             activeInstr.mHasBackgroundActivityStartsPermission = checkPermission(
                     START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
                             == PackageManager.PERMISSION_GRANTED;
+            activeInstr.mHasBackgroundForegroundServiceStartsPermission = checkPermission(
+                    START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid, callingUid)
+                            == PackageManager.PERMISSION_GRANTED;
             activeInstr.mNoRestart = noRestart;
-
             boolean disableHiddenApiChecks = ai.usesNonSdkApi()
                     || (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
             boolean disableTestApiChecks = disableHiddenApiChecks
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 1b06dd9..cf4adc6 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -359,6 +359,8 @@
     // It must obtain the proc state from a persistent/top process or FGS, not transitive.
     int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
 
+    private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>();
+
     void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
             long startTime) {
         this.startUid = startUid;
@@ -1965,6 +1967,18 @@
         }
     }
 
+    public void addAllowBackgroundFgsStartsToken(Binder entity) {
+        mBackgroundFgsStartTokens.add(entity);
+    }
+
+    public void removeAllowBackgroundFgsStartsToken(Binder entity) {
+        mBackgroundFgsStartTokens.remove(entity);
+    }
+
+    public boolean areBackgroundFgsStartsAllowedByToken() {
+        return !mBackgroundFgsStartTokens.isEmpty();
+    }
+
     ErrorDialogController getDialogController() {
         return mDialogController;
     }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 364ad21..e129561 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -147,6 +147,10 @@
     @GuardedBy("ams")
     private List<IBinder> mBgActivityStartsByStartOriginatingTokens = new ArrayList<>();
 
+    // any current binding to this service has BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND
+    // flag? if true, the process can start FGS from background.
+    boolean mIsAllowedBgFgsStartsByBinding;
+
     // allow while-in-use permissions in foreground service or not.
     // while-in-use permissions in FGS started from background might be restricted.
     boolean mAllowWhileInUsePermissionInFgs;
@@ -418,6 +422,10 @@
             pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart=");
             pw.println(mIsAllowedBgActivityStartsByStart);
         }
+        if (mIsAllowedBgFgsStartsByBinding) {
+            pw.print(prefix); pw.print("mIsAllowedBgFgsStartsByBinding=");
+            pw.println(mIsAllowedBgFgsStartsByBinding);
+        }
         pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
                 pw.println(mAllowWhileInUsePermissionInFgs);
         pw.print(prefix); pw.print("recentCallingPackage=");
@@ -600,6 +608,11 @@
             } else {
                 _proc.removeAllowBackgroundActivityStartsToken(this);
             }
+            if (mIsAllowedBgFgsStartsByBinding) {
+                _proc.addAllowBackgroundFgsStartsToken(this);
+            } else {
+                _proc.removeAllowBackgroundFgsStartsToken(this);
+            }
         }
         if (app != null && app != _proc) {
             // If the old app is allowed to start bg activities because of a service start, leave it
@@ -686,11 +699,34 @@
         setAllowedBgActivityStartsByBinding(isAllowedByBinding);
     }
 
+    void updateIsAllowedBgFgsStartsByBinding() {
+        boolean isAllowedByBinding = false;
+        for (int conni = connections.size() - 1; conni >= 0; conni--) {
+            ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
+            for (int i = 0; i < cr.size(); i++) {
+                if ((cr.get(i).flags
+                        & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) {
+                    isAllowedByBinding = true;
+                    break;
+                }
+            }
+            if (isAllowedByBinding) {
+                break;
+            }
+        }
+        setAllowedBgFgsStartsByBinding(isAllowedByBinding);
+    }
+
     void setAllowedBgActivityStartsByBinding(boolean newValue) {
         mIsAllowedBgActivityStartsByBinding = newValue;
         updateParentProcessBgActivityStartsToken();
     }
 
+    void setAllowedBgFgsStartsByBinding(boolean newValue) {
+        mIsAllowedBgFgsStartsByBinding = newValue;
+        updateParentProcessBgFgsStartsToken();
+    }
+
     /**
      * Called when the service is started with allowBackgroundActivityStarts set. We allow
      * it for background activity starts, setting up a callback to remove this ability after a
@@ -777,6 +813,17 @@
         }
     }
 
+    private void updateParentProcessBgFgsStartsToken() {
+        if (app == null) {
+            return;
+        }
+        if (mIsAllowedBgFgsStartsByBinding) {
+            app.addAllowBackgroundFgsStartsToken(this);
+        } else {
+            app.removeAllowBackgroundFgsStartsToken(this);
+        }
+    }
+
     /**
      * Returns the originating token if that's the only reason background activity starts are
      * allowed. In order for that to happen the service has to be allowed only due to starts, since