Merge "Allow FGS start for proc state BOUND_FOREGROUND_SERVICE and above."
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4a338b3..e1124ab 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
+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;
 import static android.os.Process.NFC_UID;
@@ -145,7 +146,7 @@
     private static final boolean SHOW_DUNGEON_NOTIFICATION = false;
 
     public static final int FGS_FEATURE_DENIED = 0;
-    public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 1;
+    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;
@@ -154,10 +155,12 @@
     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;
 
     @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = {
             FGS_FEATURE_DENIED,
-            FGS_FEATURE_ALLOWED_BY_PROC_STATE,
+            FGS_FEATURE_ALLOWED_BY_UID_STATE,
             FGS_FEATURE_ALLOWED_BY_UID_VISIBLE,
             FGS_FEATURE_ALLOWED_BY_FLAG,
             FGS_FEATURE_ALLOWED_BY_SYSTEM_UID,
@@ -165,7 +168,9 @@
             FGS_FEATURE_ALLOWED_BY_TOKEN,
             FGS_FEATURE_ALLOWED_BY_PERMISSION,
             FGS_FEATURE_ALLOWED_BY_WHITELIST,
-            FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER
+            FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER,
+            FGS_FEATURE_ALLOWED_BY_PROC_STATE,
+            FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface FgsFeatureRetCode {}
@@ -576,18 +581,11 @@
                 if (r.mAllowStartForeground == FGS_FEATURE_DENIED
                         && (mAm.mConstants.mFlagFgsStartRestrictionEnabled
                         || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) {
-                    if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
-                            && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
-                        // uid is on DeviceIdleController's allowlist.
-                        Slog.d(TAG, "startForegroundService() mAllowStartForeground false "
-                                + "but allowlist true: service " + r.shortInstanceName);
-                    } else {
-                        Slog.w(TAG, "startForegroundService() not allowed due to "
-                                + "mAllowStartForeground false: service "
-                                + r.shortInstanceName);
-                        showFgsBgRestrictedNotificationLocked(r);
-                        return null;
-                    }
+                    Slog.w(TAG, "startForegroundService() not allowed due to "
+                            + "mAllowStartForeground false: service "
+                            + r.shortInstanceName);
+                    showFgsBgRestrictedNotificationLocked(r);
+                    return null;
                 }
             }
         }
@@ -1488,20 +1486,12 @@
                         if (r.mAllowStartForeground == FGS_FEATURE_DENIED
                                 && (mAm.mConstants.mFlagFgsStartRestrictionEnabled
                                 || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) {
-                            if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
-                                    && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
-                                // uid is on DeviceIdleController's allowlist.
-                                Slog.d(TAG, "Service.startForeground() "
-                                        + "mAllowStartForeground false but allowlist true: service "
-                                        + r.shortInstanceName);
-                            } else {
-                                Slog.w(TAG, "Service.startForeground() not allowed due to "
-                                                + "mAllowStartForeground false: service "
-                                                + r.shortInstanceName);
-                                showFgsBgRestrictedNotificationLocked(r);
-                                updateServiceForegroundLocked(r.app, true);
-                                ignoreForeground = true;
-                            }
+                            Slog.w(TAG, "Service.startForeground() not allowed due to "
+                                            + "mAllowStartForeground false: service "
+                                            + r.shortInstanceName);
+                            showFgsBgRestrictedNotificationLocked(r);
+                            updateServiceForegroundLocked(r.app, true);
+                            ignoreForeground = true;
                         }
                     }
                 }
@@ -4944,38 +4934,39 @@
             r.mAllowWhileInUsePermissionInFgs = true;
         }
 
-        if (!r.mAllowWhileInUsePermissionInFgs || (r.mAllowStartForeground == FGS_FEATURE_DENIED)) {
-            final @FgsFeatureRetCode int temp = shouldAllowFgsFeatureLocked(callingPackage,
-                    callingPid, callingUid, intent, r, allowBackgroundActivityStarts);
+        if (!r.mAllowWhileInUsePermissionInFgs
+                || (r.mAllowStartForeground == FGS_FEATURE_DENIED)) {
+            final @FgsFeatureRetCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
+                    callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts);
             if (!r.mAllowWhileInUsePermissionInFgs) {
-                r.mAllowWhileInUsePermissionInFgs = (temp != FGS_FEATURE_DENIED);
+                r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != FGS_FEATURE_DENIED);
             }
             if (r.mAllowStartForeground == FGS_FEATURE_DENIED) {
-                r.mAllowStartForeground = temp;
+                r.mAllowStartForeground = shouldAllowFgsStartForegroundLocked(allowWhileInUse,
+                        callingPackage, callingPid, callingUid, intent, r,
+                        allowBackgroundActivityStarts);
             }
         }
     }
 
     /**
-     * Should allow FGS feature or not.
+     * Should allow while-in-use permissions in FGS or not.
+     * A typical BG started FGS is not allowed to have while-in-use permissions.
      * @param callingPackage caller app's package name.
      * @param callingUid caller app's uid.
-     * @param intent intent to start/bind service.
      * @param r the service to start.
      * @return {@link FgsFeatureRetCode}
      */
-    private @FgsFeatureRetCode int shouldAllowFgsFeatureLocked(String callingPackage,
-            int callingPid, int callingUid, Intent intent, ServiceRecord r,
+    private @FgsFeatureRetCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
+            int callingPid, int callingUid, ServiceRecord r,
             boolean allowBackgroundActivityStarts) {
         int ret = FGS_FEATURE_DENIED;
 
-        final StringBuilder sb = new StringBuilder(64);
         final int uidState = mAm.getUidState(callingUid);
         if (ret == FGS_FEATURE_DENIED) {
             // Is the calling UID at PROCESS_STATE_TOP or above?
             if (uidState <= ActivityManager.PROCESS_STATE_TOP) {
-                sb.append("uidState=").append(uidState);
-                ret = FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+                ret = FGS_FEATURE_ALLOWED_BY_UID_STATE;
             }
         }
 
@@ -5010,7 +5001,6 @@
             }
 
             if (isCallerSystem) {
-                sb.append("callingUid=").append(callingAppId);
                 ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
             }
         }
@@ -5049,6 +5039,53 @@
                 ret = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
             }
         }
+        return ret;
+    }
+
+    /**
+     * Should allow the FGS to start (AKA startForeground()) or not.
+     * The check in this method is in addition to check in
+     * {@link #shouldAllowFgsWhileInUsePermissionLocked}
+     * @param allowWhileInUse the return code from {@link #shouldAllowFgsWhileInUsePermissionLocked}
+     * @param callingPackage caller app's package name.
+     * @param callingUid caller app's uid.
+     * @param intent intent to start/bind service.
+     * @param r the service to start.
+     * @return {@link FgsFeatureRetCode}
+     */
+    private @FgsFeatureRetCode int shouldAllowFgsStartForegroundLocked(
+            @FgsFeatureRetCode int allowWhileInUse, String callingPackage, int callingPid,
+            int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
+        int ret = allowWhileInUse;
+
+        final StringBuilder sb = new StringBuilder(64);
+        final int uidState = mAm.getUidState(callingUid);
+        if (ret == FGS_FEATURE_DENIED) {
+            // Is the calling UID at PROCESS_STATE_TOP or above?
+            if (uidState <= ActivityManager.PROCESS_STATE_TOP) {
+                sb.append("uidState=").append(uidState);
+                ret = FGS_FEATURE_ALLOWED_BY_UID_STATE;
+            }
+        }
+
+        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
+                        && pr.mAllowStartFgsState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+                    ret = FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+                    break;
+                }
+            }
+        }
+
+        if (ret == FGS_FEATURE_DENIED) {
+            if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
+                    && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
+                // uid is on DeviceIdleController's allowlist.
+                ret = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
+            }
+        }
 
         final String debugInfo =
                 "[callingPackage: " + callingPackage
@@ -5071,8 +5108,8 @@
         switch (code) {
             case FGS_FEATURE_DENIED:
                 return "DENIED";
-            case FGS_FEATURE_ALLOWED_BY_PROC_STATE:
-                return "ALLOWED_BY_PROC_STATE";
+            case FGS_FEATURE_ALLOWED_BY_UID_STATE:
+                return "ALLOWED_BY_UID_STATE";
             case FGS_FEATURE_ALLOWED_BY_UID_VISIBLE:
                 return "ALLOWED_BY_UID_VISIBLE";
             case FGS_FEATURE_ALLOWED_BY_FLAG:
@@ -5089,13 +5126,17 @@
                 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";
             default:
                 return "";
         }
     }
 
     private static boolean isFgsBgStart(@FgsFeatureRetCode int code) {
-        return code != FGS_FEATURE_ALLOWED_BY_PROC_STATE
+        return code != FGS_FEATURE_ALLOWED_BY_UID_STATE
                 && code != FGS_FEATURE_ALLOWED_BY_UID_VISIBLE;
     }
 
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 01d0a6d..771f273 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1329,6 +1329,8 @@
         app.setCached(false);
         app.shouldNotFreeze = false;
 
+        app.mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
+
         final int appUid = app.info.uid;
         final int logUid = mService.mCurOomAdjUid;
 
@@ -1349,6 +1351,7 @@
             app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
             app.curCapability = PROCESS_CAPABILITY_ALL;
             app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
+            app.bumpAllowStartFgsState(PROCESS_STATE_PERSISTENT);
             // System processes can do UI, and when they do we want to have
             // them trim their memory after the user leaves the UI.  To
             // facilitate this, here we need to determine whether or not it
@@ -1403,6 +1406,7 @@
             app.adjType = "top-activity";
             foregroundActivities = true;
             procState = PROCESS_STATE_CUR_TOP;
+            app.bumpAllowStartFgsState(PROCESS_STATE_TOP);
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app);
             }
@@ -1500,6 +1504,7 @@
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                 procState = PROCESS_STATE_FOREGROUND_SERVICE;
+                app.bumpAllowStartFgsState(PROCESS_STATE_FOREGROUND_SERVICE);
                 app.adjType = "fg-service";
                 app.setCached(false);
                 schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
@@ -1887,7 +1892,9 @@
                                 // into the top state, since they are not on top.  Instead
                                 // give them the best bound state after that.
                                 if (cr.hasFlag(Context.BIND_FOREGROUND_SERVICE)) {
-                                    clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;                                                                                                                       ;
+                                    clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                                    app.bumpAllowStartFgsState(
+                                            PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
                                 } else if (mService.mWakefulness
                                         == PowerManagerInternal.WAKEFULNESS_AWAKE
                                         && (cr.flags & Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
@@ -1901,6 +1908,7 @@
                                 // Go at most to BOUND_TOP, unless requested to elevate
                                 // to client's state.
                                 clientProcState = PROCESS_STATE_BOUND_TOP;
+                                app.bumpAllowStartFgsState(PROCESS_STATE_BOUND_TOP);
                                 boolean enabled = false;
                                 try {
                                     enabled = mPlatformCompatCache.isChangeEnabled(
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 5dbaaaf..85c5bdc 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -353,6 +353,10 @@
 
     long mKillTime; // The timestamp in uptime when this process was killed.
 
+    // If the proc state is PROCESS_STATE_BOUND_FOREGROUND_SERVICE or above, it can start FGS.
+    // It must obtain the proc state from a persistent/top process or FGS, not transitive.
+    int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
+
     void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
             long startTime) {
         this.startUid = startUid;
@@ -466,6 +470,8 @@
                 pw.print(" setCapability=");
                 ActivityManager.printCapabilitiesFull(pw, setCapability);
                 pw.println();
+        pw.print(prefix); pw.print("allowStartFgsState=");
+                pw.println(mAllowStartFgsState);
         if (hasShownUi || mPendingUiClean || hasAboveClient || treatLikeActivity) {
             pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
                     pw.print(" pendingUiClean="); pw.print(mPendingUiClean);
@@ -1942,6 +1948,12 @@
         return mDialogController;
     }
 
+    void bumpAllowStartFgsState(int newProcState) {
+        if (newProcState < mAllowStartFgsState) {
+            mAllowStartFgsState = newProcState;
+        }
+    }
+
     /** A controller to generate error dialogs in {@link ProcessRecord} */
     class ErrorDialogController {
         /** dialogs being displayed due to crash */