Allow-while-idle alarms throttling for app standby

Started throttling allow_while_idle alarms again. Throttled
allow_while_idle alarms behave as if the scheduling app scheduled it at
the next eligible time, so there is no conflict with app standby
rescheduling. Also exempting alarms by mSystemUiUid from app standby

Test: atest CtsAlarmManagerTestCases:AppStandbyTests

Bug: 72660630
Bug: 72916765
Change-Id: Ibb8ab449e1c148fc9689f1c2afb89b8d9630edfa
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 17c617c..45b2118 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -88,6 +88,7 @@
 import java.util.function.Predicate;
 
 import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
 import static android.app.AlarmManager.RTC_WAKEUP;
 import static android.app.AlarmManager.RTC;
 import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
@@ -1011,7 +1012,7 @@
             // Recurring alarms may have passed several alarm intervals while the
             // alarm was kept pending. Send the appropriate trigger count.
             if (alarm.repeatInterval > 0) {
-                alarm.count += (nowELAPSED - alarm.requestedWhenElapsed) / alarm.repeatInterval;
+                alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
                 // Also schedule its next recurrence
                 final long delta = alarm.count * alarm.repeatInterval;
                 final long nextElapsed = alarm.whenElapsed + delta;
@@ -1507,24 +1508,19 @@
      * Adjusts the alarm delivery time based on the current app standby bucket.
      * @param alarm The alarm to adjust
      * @return true if the alarm delivery time was updated.
-     * TODO: Reduce the number of calls to getAppStandbyBucket by batching the calls per
-     * {package, user} pairs
      */
     private boolean adjustDeliveryTimeBasedOnStandbyBucketLocked(Alarm alarm) {
-        if (alarm.alarmClock != null || UserHandle.isCore(alarm.creatorUid)) {
-            return false;
-        }
-        // TODO: short term fix for b/72816079, remove after a proper fix is in place
-        if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+        if (isExemptFromAppStandby(alarm)) {
             return false;
         }
         if (mAppStandbyParole) {
-            if (alarm.whenElapsed > alarm.requestedWhenElapsed) {
+            if (alarm.whenElapsed > alarm.expectedWhenElapsed) {
                 // We did defer this alarm earlier, restore original requirements
-                alarm.whenElapsed = alarm.requestedWhenElapsed;
-                alarm.maxWhenElapsed = alarm.requestedMaxWhenElapsed;
+                alarm.whenElapsed = alarm.expectedWhenElapsed;
+                alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
+                return true;
             }
-            return true;
+            return false;
         }
         final long oldWhenElapsed = alarm.whenElapsed;
         final long oldMaxWhenElapsed = alarm.maxWhenElapsed;
@@ -1538,13 +1534,13 @@
         final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L);
         if (lastElapsed > 0) {
             final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
-            if (alarm.requestedWhenElapsed < minElapsed) {
+            if (alarm.expectedWhenElapsed < minElapsed) {
                 alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
             } else {
                 // app is now eligible to run alarms at the originally requested window.
                 // Restore original requirements in case they were changed earlier.
-                alarm.whenElapsed = alarm.requestedWhenElapsed;
-                alarm.maxWhenElapsed = alarm.requestedMaxWhenElapsed;
+                alarm.whenElapsed = alarm.expectedWhenElapsed;
+                alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
             }
         }
         return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed);
@@ -1728,7 +1724,7 @@
             // This means we will allow these alarms to go off as normal even while idle, with no
             // timing restrictions.
             } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
-                    || callingUid == mSystemUiUid
+                    || UserHandle.isSameApp(callingUid, mSystemUiUid)
                     || mForceAppStandbyTracker.isUidPowerSaveWhitelisted(callingUid))) {
                 flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
                 flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
@@ -3000,10 +2996,11 @@
                         // 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
                         // correct time period.
-                        alarm.whenElapsed = minTime;
+                        alarm.expectedWhenElapsed = alarm.whenElapsed = minTime;
                         if (alarm.maxWhenElapsed < minTime) {
                             alarm.maxWhenElapsed = minTime;
                         }
+                        alarm.expectedMaxWhenElapsed = alarm.maxWhenElapsed;
                         if (RECORD_DEVICE_IDLE_ALARMS) {
                             IdleDispatchEntry ent = new IdleDispatchEntry();
                             ent.uid = alarm.uid;
@@ -3053,7 +3050,7 @@
                 if (alarm.repeatInterval > 0) {
                     // this adjustment will be zero if we're late by
                     // less than one full repeat interval
-                    alarm.count += (nowELAPSED - alarm.requestedWhenElapsed) / alarm.repeatInterval;
+                    alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
 
                     // Also schedule its next recurrence
                     final long delta = alarm.count * alarm.repeatInterval;
@@ -3128,8 +3125,9 @@
         public long windowLength;
         public long whenElapsed;    // 'when' in the elapsed time base
         public long maxWhenElapsed; // also in the elapsed time base
-        public final long requestedWhenElapsed; // original expiry time requested by the app
-        public final long requestedMaxWhenElapsed;
+        // Expected alarm expiry time before app standby deferring is applied.
+        public long expectedWhenElapsed;
+        public long expectedMaxWhenElapsed;
         public long repeatInterval;
         public PriorityClass priorityClass;
 
@@ -3143,10 +3141,10 @@
                     || _type == AlarmManager.RTC_WAKEUP;
             when = _when;
             whenElapsed = _whenElapsed;
-            requestedWhenElapsed = _whenElapsed;
+            expectedWhenElapsed = _whenElapsed;
             windowLength = _windowLength;
             maxWhenElapsed = _maxWhen;
-            requestedMaxWhenElapsed = _maxWhen;
+            expectedMaxWhenElapsed = _maxWhen;
             repeatInterval = _interval;
             operation = _op;
             listener = _rec;
@@ -3205,8 +3203,10 @@
             final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
             pw.print(prefix); pw.print("tag="); pw.println(statsTag);
             pw.print(prefix); pw.print("type="); pw.print(type);
-                    pw.print(" requestedWhenElapsed="); TimeUtils.formatDuration(
-                            requestedWhenElapsed, nowELAPSED, pw);
+                    pw.print(" expectedWhenElapsed="); TimeUtils.formatDuration(
+                    expectedWhenElapsed, nowELAPSED, pw);
+                    pw.print(" expectedMaxWhenElapsed="); TimeUtils.formatDuration(
+                    expectedMaxWhenElapsed, nowELAPSED, pw);
                     pw.print(" whenElapsed="); TimeUtils.formatDuration(whenElapsed,
                             nowELAPSED, pw);
                     pw.print(" when=");
@@ -3344,6 +3344,11 @@
         }
     }
 
+    private boolean isExemptFromAppStandby(Alarm a) {
+        return a.alarmClock != null || UserHandle.isCore(a.creatorUid)
+                || (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0;
+    }
+
     private class AlarmThread extends Thread
     {
         public AlarmThread()
@@ -3462,7 +3467,7 @@
                                     new ArraySet<>();
                             for (int i = 0; i < triggerList.size(); i++) {
                                 final Alarm a = triggerList.get(i);
-                                if (!UserHandle.isCore(a.creatorUid)) {
+                                if (!isExemptFromAppStandby(a)) {
                                     triggerPackages.add(Pair.create(
                                             a.sourcePackage, UserHandle.getUserId(a.creatorUid)));
                                 }
@@ -4148,7 +4153,7 @@
                     mAllowWhileIdleDispatches.add(ent);
                 }
             }
-            if (!UserHandle.isCore(alarm.creatorUid)) {
+            if (!isExemptFromAppStandby(alarm)) {
                 final Pair<String, Integer> packageUser = Pair.create(alarm.sourcePackage,
                         UserHandle.getUserId(alarm.creatorUid));
                 mLastAlarmDeliveredForPackage.put(packageUser, nowELAPSED);