While-idle alarm timeout & EBS

Use the long whileidle timeout for apps in the background.

Test: Manual test with a test app (I9d0c0ed4b6a0)
Test: atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
Test: atest $ANDROID_BUILD_TOP/cts/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverAlarmTest.java
Bug: 72124522
Change-Id: Ibd0a46e573604f7f2d1ec51a60ea87c163233003
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 30dfee8..f49cd67 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1840,14 +1840,6 @@
             if (!blocked) {
                 pw.println("    none");
             }
-            pw.print("  mUseAllowWhileIdleShortTime: [");
-            for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
-                if (mUseAllowWhileIdleShortTime.valueAt(i)) {
-                    UserHandle.formatUid(pw, mUseAllowWhileIdleShortTime.keyAt(i));
-                    pw.print(" ");
-                }
-            }
-            pw.println("]");
 
             pw.println("  mLastAlarmDeliveredForPackage:");
             for (int i = 0; i < mLastAlarmDeliveredForPackage.size(); i++) {
@@ -1913,14 +1905,32 @@
             if (mLastAllowWhileIdleDispatch.size() > 0) {
                 pw.println("  Last allow while idle dispatch times:");
                 for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) {
-                    pw.print("  UID ");
-                    UserHandle.formatUid(pw, mLastAllowWhileIdleDispatch.keyAt(i));
+                    pw.print("    UID ");
+                    final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
+                    UserHandle.formatUid(pw, uid);
                     pw.print(": ");
-                    TimeUtils.formatDuration(mLastAllowWhileIdleDispatch.valueAt(i),
-                            nowELAPSED, pw);
+                    final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
+                    TimeUtils.formatDuration(lastTime, nowELAPSED, pw);
+
+                    final long minInterval = getWhileIdleMinIntervalLocked(uid);
+                    pw.print("  Next allowed:");
+                    TimeUtils.formatDuration(lastTime + minInterval, nowELAPSED, pw);
+                    pw.print(" (");
+                    TimeUtils.formatDuration(minInterval, 0, pw);
+                    pw.print(")");
+
                     pw.println();
                 }
             }
+
+            pw.print("  mUseAllowWhileIdleShortTime: [");
+            for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
+                if (mUseAllowWhileIdleShortTime.valueAt(i)) {
+                    UserHandle.formatUid(pw, mUseAllowWhileIdleShortTime.keyAt(i));
+                    pw.print(" ");
+                }
+            }
+            pw.println("]");
             pw.println();
 
             if (mLog.dump(pw, "  Recent problems", "    ")) {
@@ -2181,13 +2191,23 @@
             for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); ++i) {
                 final long token = proto.start(
                         AlarmManagerServiceProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
-                proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.UID,
-                        mLastAllowWhileIdleDispatch.keyAt(i));
-                proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.TIME_MS,
-                        mLastAllowWhileIdleDispatch.valueAt(i));
+                final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
+                final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
+
+                proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.UID, uid);
+                proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.TIME_MS, lastTime);
+                proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.NEXT_ALLOWED_MS,
+                        lastTime + getWhileIdleMinIntervalLocked(uid));
                 proto.end(token);
             }
 
+            for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
+                if (mUseAllowWhileIdleShortTime.valueAt(i)) {
+                    proto.write(AlarmManagerServiceProto.USE_ALLOW_WHILE_IDLE_SHORT_TIME,
+                            mUseAllowWhileIdleShortTime.keyAt(i));
+                }
+            }
+
             mLog.writeToProto(proto, AlarmManagerServiceProto.RECENT_PROBLEMS);
 
             final FilterStats[] topFilters = new FilterStats[10];
@@ -2866,6 +2886,23 @@
     private native int setKernelTime(long nativeData, long millis);
     private native int setKernelTimezone(long nativeData, int minuteswest);
 
+    private long getWhileIdleMinIntervalLocked(int uid) {
+        final boolean dozing = mPendingIdleUntil != null;
+        final boolean ebs = mForceAppStandbyTracker.isForceAllAppsStandbyEnabled();
+        if (!dozing && !ebs) {
+            return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
+        }
+        if (dozing) {
+            return mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+        }
+        if (mUseAllowWhileIdleShortTime.get(uid)) {
+            // if the last allow-while-idle went off while uid was fg, or the uid
+            // recently came into fg, don't block the alarm for long.
+            return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
+        }
+        return mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+    }
+
     boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
             final long nowRTC) {
         boolean hasWakeup = false;
@@ -2891,20 +2928,7 @@
                     // If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can
                     // schedule such alarms.
                     final long lastTime = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, 0);
-                    final boolean dozing = mPendingIdleUntil != null;
-                    final boolean ebs = mForceAppStandbyTracker.isForceAllAppsStandbyEnabled();
-                    final long minTime;
-                    if (!dozing && !ebs) {
-                        minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
-                    } else if (dozing) {
-                        minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
-                    } else if (mUseAllowWhileIdleShortTime.get(alarm.creatorUid)) {
-                        // if the last allow-while-idle went off while uid was fg, or the uid
-                        // recently came into fg, don't block the alarm for long.
-                        minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
-                    } else {
-                        minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
-                    }
+                    final long minTime = lastTime + getWhileIdleMinIntervalLocked(alarm.creatorUid);
                     if (nowELAPSED < minTime) {
                         // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
                         // alarm went off for this app.  Reschedule the alarm to be in the
@@ -3641,6 +3665,7 @@
                 } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
                     if (uid >= 0) {
                         mLastAllowWhileIdleDispatch.delete(uid);
+                        mUseAllowWhileIdleShortTime.delete(uid);
                     }
                 } else {
                     if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
@@ -3693,7 +3718,6 @@
 
         @Override public void onUidGone(int uid, boolean disabled) {
             synchronized (mLock) {
-                mUseAllowWhileIdleShortTime.delete(uid);
                 if (disabled) {
                     removeForStoppedLocked(uid);
                 }
@@ -3701,9 +3725,6 @@
         }
 
         @Override public void onUidActive(int uid) {
-            synchronized (mLock) {
-                mUseAllowWhileIdleShortTime.put(uid, true);
-            }
         }
 
         @Override public void onUidIdle(int uid, boolean disabled) {
@@ -3766,6 +3787,18 @@
                 sendPendingBackgroundAlarmsLocked(uid, packageName);
             }
         }
+
+        @Override
+        public void onUidForeground(int uid, boolean foreground) {
+            synchronized (mLock) {
+                if (foreground) {
+                    mUseAllowWhileIdleShortTime.put(uid, true);
+
+                    // Note we don't have to drain the pending while-idle alarms here, because
+                    // this event should coincide with unblockAlarmsForUid().
+                }
+            }
+        }
     };
 
     private final BroadcastStats getStatsLocked(PendingIntent pi) {
@@ -4026,7 +4059,7 @@
             if (allowWhileIdle) {
                 // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
                 mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
-                if (mForceAppStandbyTracker.isInForeground(alarm.creatorUid)) {
+                if (mForceAppStandbyTracker.isUidInForeground(alarm.creatorUid)) {
                     mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true);
                 } else {
                     mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false);
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 7604044..339101f 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -54,6 +54,7 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
+import com.android.server.DeviceIdleController.LocalService;
 import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage;
 import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
 
@@ -64,18 +65,15 @@
 /**
  * Class to keep track of the information related to "force app standby", which includes:
  * - OP_RUN_ANY_IN_BACKGROUND for each package
- * - UID foreground state
+ * - UID foreground/active state
  * - User+system power save whitelist
  * - Temporary power save whitelist
  * - Global "force all apps standby" mode enforced by battery saver.
  *
- * TODO: In general, we can reduce the number of callbacks by checking all signals before sending
- * each callback. For example, even when an UID comes into the foreground, if it wasn't
- * originally restricted, then there's no need to send an event.
- * Doing this would be error-prone, so we punt it for now, but we should revisit it later.
+ * TODO: Make it a LocalService.
  *
  * Test:
- * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+  atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
  */
 public class ForceAppStandbyTracker {
     private static final String TAG = "ForceAppStandbyTracker";
@@ -108,6 +106,11 @@
     @GuardedBy("mLock")
     final ArraySet<Pair<Integer, String>> mRunAnyRestrictedPackages = new ArraySet<>();
 
+    /** UIDs that are active. */
+    @GuardedBy("mLock")
+    final SparseBooleanArray mActiveUids = new SparseBooleanArray();
+
+    /** UIDs that are in the foreground. */
     @GuardedBy("mLock")
     final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
 
@@ -160,18 +163,20 @@
     boolean mForcedAppStandbyEnabled;
 
     interface Stats {
-        int UID_STATE_CHANGED = 0;
-        int RUN_ANY_CHANGED = 1;
-        int ALL_UNWHITELISTED = 2;
-        int ALL_WHITELIST_CHANGED = 3;
-        int TEMP_WHITELIST_CHANGED = 4;
-        int EXEMPT_CHANGED = 5;
-        int FORCE_ALL_CHANGED = 6;
-        int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 7;
+        int UID_FG_STATE_CHANGED = 0;
+        int UID_ACTIVE_STATE_CHANGED = 1;
+        int RUN_ANY_CHANGED = 2;
+        int ALL_UNWHITELISTED = 3;
+        int ALL_WHITELIST_CHANGED = 4;
+        int TEMP_WHITELIST_CHANGED = 5;
+        int EXEMPT_CHANGED = 6;
+        int FORCE_ALL_CHANGED = 7;
+        int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
     }
 
     private final StatLogger mStatLogger = new StatLogger(new String[] {
-            "UID_STATE_CHANGED",
+            "UID_FG_STATE_CHANGED",
+            "UID_ACTIVE_STATE_CHANGED",
             "RUN_ANY_CHANGED",
             "ALL_UNWHITELISTED",
             "ALL_WHITELIST_CHANGED",
@@ -260,9 +265,16 @@
          * This is called when the foreground state changed for a UID.
          */
         private void onUidForegroundStateChanged(ForceAppStandbyTracker sender, int uid) {
+            onUidForeground(uid, sender.isUidInForeground(uid));
+        }
+
+        /**
+         * This is called when the active/idle state changed for a UID.
+         */
+        private void onUidActiveStateChanged(ForceAppStandbyTracker sender, int uid) {
             updateJobsForUid(uid);
 
-            if (sender.isInForeground(uid)) {
+            if (sender.isUidActive(uid)) {
                 unblockAlarmsForUid(uid);
             }
         }
@@ -355,6 +367,14 @@
          */
         public void unblockAlarmsForUidPackage(int uid, String packageName) {
         }
+
+        /**
+         * Called when a UID comes into the foreground or the background.
+         *
+         * @see #isUidInForeground(int)
+         */
+        public void onUidForeground(int uid, boolean foreground) {
+        }
     }
 
     @VisibleForTesting
@@ -404,8 +424,10 @@
 
             try {
                 mIActivityManager.registerUidObserver(new UidObserver(),
-                        ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
-                                | ActivityManager.UID_OBSERVER_ACTIVE,
+                        ActivityManager.UID_OBSERVER_GONE
+                                | ActivityManager.UID_OBSERVER_IDLE
+                                | ActivityManager.UID_OBSERVER_ACTIVE
+                                | ActivityManager.UID_OBSERVER_PROCSTATE,
                         ActivityManager.PROCESS_STATE_UNKNOWN, null);
                 mAppOpsService.startWatchingMode(TARGET_OP, null,
                         new AppOpsWatcher());
@@ -563,65 +585,77 @@
         return true;
     }
 
-    /**
-     * Puts a UID to {@link #mForegroundUids}.
-     */
-    void uidToForeground(int uid) {
-        synchronized (mLock) {
-            if (UserHandle.isCore(uid)) {
-                return;
-            }
-            // TODO This can be optimized by calling indexOfKey and sharing the index for get and
-            // put.
-            if (mForegroundUids.get(uid)) {
-                return;
-            }
-            mForegroundUids.put(uid, true);
-            mHandler.notifyUidForegroundStateChanged(uid);
+    private static boolean addUidToArray(SparseBooleanArray array, int uid) {
+        if (UserHandle.isCore(uid)) {
+            return false;
         }
+        if (array.get(uid)) {
+            return false;
+        }
+        array.put(uid, true);
+        return true;
     }
 
-    /**
-     * Sets false for a UID {@link #mForegroundUids}, or remove it when {@code remove} is true.
-     */
-    void uidToBackground(int uid, boolean remove) {
-        synchronized (mLock) {
-            if (UserHandle.isCore(uid)) {
-                return;
-            }
-            // TODO This can be optimized by calling indexOfKey and sharing the index for get and
-            // put.
-            if (!mForegroundUids.get(uid)) {
-                return;
-            }
-            if (remove) {
-                mForegroundUids.delete(uid);
-            } else {
-                mForegroundUids.put(uid, false);
-            }
-            mHandler.notifyUidForegroundStateChanged(uid);
+    private static boolean removeUidFromArray(SparseBooleanArray array, int uid, boolean remove) {
+        if (UserHandle.isCore(uid)) {
+            return false;
         }
+        if (!array.get(uid)) {
+            return false;
+        }
+        if (remove) {
+            array.delete(uid);
+        } else {
+            array.put(uid, false);
+        }
+        return true;
     }
 
     private final class UidObserver extends IUidObserver.Stub {
         @Override
         public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+            synchronized (mLock) {
+                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                    if (removeUidFromArray(mForegroundUids, uid, false)) {
+                        mHandler.notifyUidForegroundStateChanged(uid);
+                    }
+                } else {
+                    if (addUidToArray(mForegroundUids, uid)) {
+                        mHandler.notifyUidForegroundStateChanged(uid);
+                    }
+                }
+            }
         }
 
         @Override
         public void onUidGone(int uid, boolean disabled) {
-            uidToBackground(uid, /*remove=*/ true);
+            removeUid(uid, true);
         }
 
         @Override
         public void onUidActive(int uid) {
-            uidToForeground(uid);
+            synchronized (mLock) {
+                if (addUidToArray(mActiveUids, uid)) {
+                    mHandler.notifyUidActiveStateChanged(uid);
+                }
+            }
         }
 
         @Override
         public void onUidIdle(int uid, boolean disabled) {
             // Just to avoid excessive memcpy, don't remove from the array in this case.
-            uidToBackground(uid, /*remove=*/ false);
+            removeUid(uid, false);
+        }
+
+        private void removeUid(int uid, boolean remove) {
+            synchronized (mLock) {
+                if (removeUidFromArray(mActiveUids, uid, remove)) {
+                    mHandler.notifyUidActiveStateChanged(uid);
+                }
+                if (removeUidFromArray(mForegroundUids, uid, remove)) {
+                    mHandler.notifyUidForegroundStateChanged(uid);
+                }
+            }
         }
 
         @Override
@@ -695,22 +729,27 @@
     }
 
     private class MyHandler extends Handler {
-        private static final int MSG_UID_STATE_CHANGED = 1;
-        private static final int MSG_RUN_ANY_CHANGED = 2;
-        private static final int MSG_ALL_UNWHITELISTED = 3;
-        private static final int MSG_ALL_WHITELIST_CHANGED = 4;
-        private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
-        private static final int MSG_FORCE_ALL_CHANGED = 6;
-        private static final int MSG_USER_REMOVED = 7;
-        private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
-        private static final int MSG_EXEMPT_CHANGED = 9;
+        private static final int MSG_UID_ACTIVE_STATE_CHANGED = 0;
+        private static final int MSG_UID_FG_STATE_CHANGED = 1;
+        private static final int MSG_RUN_ANY_CHANGED = 3;
+        private static final int MSG_ALL_UNWHITELISTED = 4;
+        private static final int MSG_ALL_WHITELIST_CHANGED = 5;
+        private static final int MSG_TEMP_WHITELIST_CHANGED = 6;
+        private static final int MSG_FORCE_ALL_CHANGED = 7;
+        private static final int MSG_USER_REMOVED = 8;
+        private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 9;
+        private static final int MSG_EXEMPT_CHANGED = 10;
 
         public MyHandler(Looper looper) {
             super(looper);
         }
 
+        public void notifyUidActiveStateChanged(int uid) {
+            obtainMessage(MSG_UID_ACTIVE_STATE_CHANGED, uid, 0).sendToTarget();
+        }
+
         public void notifyUidForegroundStateChanged(int uid) {
-            obtainMessage(MSG_UID_STATE_CHANGED, uid, 0).sendToTarget();
+            obtainMessage(MSG_UID_FG_STATE_CHANGED, uid, 0).sendToTarget();
         }
 
         public void notifyRunAnyAppOpsChanged(int uid, @NonNull String packageName) {
@@ -718,26 +757,32 @@
         }
 
         public void notifyAllUnwhitelisted() {
+            removeMessages(MSG_ALL_UNWHITELISTED);
             obtainMessage(MSG_ALL_UNWHITELISTED).sendToTarget();
         }
 
         public void notifyAllWhitelistChanged() {
+            removeMessages(MSG_ALL_WHITELIST_CHANGED);
             obtainMessage(MSG_ALL_WHITELIST_CHANGED).sendToTarget();
         }
 
         public void notifyTempWhitelistChanged() {
+            removeMessages(MSG_TEMP_WHITELIST_CHANGED);
             obtainMessage(MSG_TEMP_WHITELIST_CHANGED).sendToTarget();
         }
 
         public void notifyForceAllAppsStandbyChanged() {
+            removeMessages(MSG_FORCE_ALL_CHANGED);
             obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
         }
 
         public void notifyForcedAppStandbyFeatureFlagChanged() {
+            removeMessages(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED);
             obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
         }
 
         public void notifyExemptChanged() {
+            removeMessages(MSG_EXEMPT_CHANGED);
             obtainMessage(MSG_EXEMPT_CHANGED).sendToTarget();
         }
 
@@ -763,11 +808,18 @@
 
             long start = mStatLogger.getTime();
             switch (msg.what) {
-                case MSG_UID_STATE_CHANGED:
+                case MSG_UID_ACTIVE_STATE_CHANGED:
+                    for (Listener l : cloneListeners()) {
+                        l.onUidActiveStateChanged(sender, msg.arg1);
+                    }
+                    mStatLogger.logDurationStat(Stats.UID_ACTIVE_STATE_CHANGED, start);
+                    return;
+
+                case MSG_UID_FG_STATE_CHANGED:
                     for (Listener l : cloneListeners()) {
                         l.onUidForegroundStateChanged(sender, msg.arg1);
                     }
-                    mStatLogger.logDurationStat(Stats.UID_STATE_CHANGED, start);
+                    mStatLogger.logDurationStat(Stats.UID_FG_STATE_CHANGED, start);
                     return;
 
                 case MSG_RUN_ANY_CHANGED:
@@ -846,18 +898,23 @@
                     mRunAnyRestrictedPackages.removeAt(i);
                 }
             }
-            for (int i = mForegroundUids.size() - 1; i >= 0; i--) {
-                final int uid = mForegroundUids.keyAt(i);
-                final int userId = UserHandle.getUserId(uid);
-
-                if (userId == removedUserId) {
-                    mForegroundUids.removeAt(i);
-                }
-            }
+            cleanUpArrayForUser(mActiveUids, removedUserId);
+            cleanUpArrayForUser(mForegroundUids, removedUserId);
             mExemptedPackages.remove(removedUserId);
         }
     }
 
+    private void cleanUpArrayForUser(SparseBooleanArray array, int removedUserId) {
+        for (int i = array.size() - 1; i >= 0; i--) {
+            final int uid = array.keyAt(i);
+            final int userId = UserHandle.getUserId(uid);
+
+            if (userId == removedUserId) {
+                array.removeAt(i);
+            }
+        }
+    }
+
     /**
      * Called by device idle controller to update the power save whitelists.
      */
@@ -954,7 +1011,7 @@
      */
     private boolean isRestricted(int uid, @NonNull String packageName,
             boolean useTempWhitelistToo, boolean exemptOnBatterySaver) {
-        if (isInForeground(uid)) {
+        if (isUidActive(uid)) {
             return false;
         }
         synchronized (mLock) {
@@ -982,13 +1039,29 @@
     }
 
     /**
+     * @return whether a UID is in active or not.
+     *
+     * Note this information is based on the UID proc state callback, meaning it's updated
+     * asynchronously and may subtly be stale. If the fresh data is needed, use
+     * {@link ActivityManagerInternal#getUidProcessState} instead.
+     */
+    public boolean isUidActive(int uid) {
+        if (UserHandle.isCore(uid)) {
+            return true;
+        }
+        synchronized (mLock) {
+            return mActiveUids.get(uid);
+        }
+    }
+
+    /**
      * @return whether a UID is in the foreground or not.
      *
      * Note this information is based on the UID proc state callback, meaning it's updated
      * asynchronously and may subtly be stale. If the fresh data is needed, use
      * {@link ActivityManagerInternal#getUidProcessState} instead.
      */
-    public boolean isInForeground(int uid) {
+    public boolean isUidInForeground(int uid) {
         if (UserHandle.isCore(uid)) {
             return true;
         }
@@ -1062,17 +1135,12 @@
             pw.println(mIsPluggedIn);
 
             pw.print(indent);
-            pw.print("Foreground uids: [");
+            pw.print("Active uids: ");
+            dumpUids(pw, mActiveUids);
 
-            String sep = "";
-            for (int i = 0; i < mForegroundUids.size(); i++) {
-                if (mForegroundUids.valueAt(i)) {
-                    pw.print(sep);
-                    pw.print(UserHandle.formatUid(mForegroundUids.keyAt(i)));
-                    sep = " ";
-                }
-            }
-            pw.println("]");
+            pw.print(indent);
+            pw.print("Foreground uids: ");
+            dumpUids(pw, mForegroundUids);
 
             pw.print(indent);
             pw.print("Whitelist appids: ");
@@ -1114,6 +1182,20 @@
         }
     }
 
+    private void dumpUids(PrintWriter pw, SparseBooleanArray array) {
+        pw.print("[");
+
+        String sep = "";
+        for (int i = 0; i < array.size(); i++) {
+            if (array.valueAt(i)) {
+                pw.print(sep);
+                pw.print(UserHandle.formatUid(array.keyAt(i)));
+                sep = " ";
+            }
+        }
+        pw.println("]");
+    }
+
     public void dumpProto(ProtoOutputStream proto, long fieldId) {
         synchronized (mLock) {
             final long token = proto.start(fieldId);
@@ -1125,6 +1207,13 @@
                     mForceAllAppStandbyForSmallBattery);
             proto.write(ForceAppStandbyTrackerProto.IS_PLUGGED_IN, mIsPluggedIn);
 
+            for (int i = 0; i < mActiveUids.size(); i++) {
+                if (mActiveUids.valueAt(i)) {
+                    proto.write(ForceAppStandbyTrackerProto.ACTIVE_UIDS,
+                            mActiveUids.keyAt(i));
+                }
+            }
+
             for (int i = 0; i < mForegroundUids.size(); i++) {
                 if (mForegroundUids.valueAt(i)) {
                     proto.write(ForceAppStandbyTrackerProto.FOREGROUND_UIDS,
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index 2e4567a..5eb7700 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -92,7 +92,7 @@
             jobStatus.printUniqueId(pw);
             pw.print(" from ");
             UserHandle.formatUid(pw, uid);
-            pw.print(mForceAppStandbyTracker.isInForeground(uid) ? " foreground" : " background");
+            pw.print(mForceAppStandbyTracker.isUidActive(uid) ? " active" : " idle");
             if (mForceAppStandbyTracker.isUidPowerSaveWhitelisted(uid) ||
                     mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(uid)) {
                 pw.print(", whitelisted");
@@ -136,7 +136,7 @@
             proto.write(TrackedJob.SOURCE_PACKAGE_NAME, sourcePkg);
 
             proto.write(TrackedJob.IS_IN_FOREGROUND,
-                    mForceAppStandbyTracker.isInForeground(sourceUid));
+                    mForceAppStandbyTracker.isUidActive(sourceUid));
             proto.write(TrackedJob.IS_WHITELISTED,
                     mForceAppStandbyTracker.isUidPowerSaveWhitelisted(sourceUid) ||
                     mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(sourceUid));