Exempting allow-while-idle alarms in EBS
Exempting allow-while-idle alarms when the device is in Extreme battery
saver. The throttling of one allow-while-idle alarm per
ALLOW_WHILE_IDLE_LONG_TIME is applied even when the device may not be
dozing. The throttling is relaxed to ALLOW_WHILE_IDLE_SHORT_TIME if the
uid was in the foreground recently.
Test: atest android.alarmmanager.cts.BatterySaverTests
Bug: 72124522
Change-Id: Ic75bac6234745c73a345180387c83bc725823aa8
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 15c0f3c..342b48e 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -82,6 +82,7 @@
import java.util.TreeSet;
import java.util.function.Predicate;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
@@ -175,7 +176,6 @@
long mNextNonWakeupDeliveryTime;
long mLastTimeChangeClockTime;
long mLastTimeChangeRealtime;
- long mAllowWhileIdleMinTime;
int mNumTimeChanged;
// Bookkeeping about the identity of the "System UI" package, determined at runtime.
@@ -199,6 +199,12 @@
*/
final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray();
+ /**
+ * For each uid, we store whether the last allow-while-idle alarm was dispatched while
+ * the uid was in foreground or not. We will use the allow_while_idle_short_time in such cases.
+ */
+ final SparseBooleanArray mUseAllowWhileIdleShortTime = new SparseBooleanArray();
+
final static class IdleDispatchEntry {
int uid;
String pkg;
@@ -242,7 +248,6 @@
private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
= "allow_while_idle_whitelist_duration";
private static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
- private static final String KEY_BG_RESTRICTIONS_ENABLED = "limit_bg_alarms_enabled";
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
@@ -277,7 +282,6 @@
public Constants(Handler handler) {
super(handler);
- updateAllowWhileIdleMinTimeLocked();
updateAllowWhileIdleWhitelistDurationLocked();
}
@@ -288,11 +292,6 @@
updateConstants();
}
- public void updateAllowWhileIdleMinTimeLocked() {
- mAllowWhileIdleMinTime = mPendingIdleUntil != null
- ? ALLOW_WHILE_IDLE_LONG_TIME : ALLOW_WHILE_IDLE_SHORT_TIME;
- }
-
public void updateAllowWhileIdleWhitelistDurationLocked() {
if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) {
mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
@@ -330,7 +329,6 @@
LISTENER_TIMEOUT = mParser.getLong(KEY_LISTENER_TIMEOUT,
DEFAULT_LISTENER_TIMEOUT);
- updateAllowWhileIdleMinTimeLocked();
updateAllowWhileIdleWhitelistDurationLocked();
}
}
@@ -967,9 +965,6 @@
}
}
- // Make sure we are using the correct ALLOW_WHILE_IDLE min time.
- mConstants.updateAllowWhileIdleMinTimeLocked();
-
// Reschedule everything.
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
@@ -1421,7 +1416,6 @@
return;
}
}
-
if (RECORD_DEVICE_IDLE_ALARMS) {
if ((a.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
IdleDispatchEntry ent = new IdleDispatchEntry();
@@ -1472,7 +1466,6 @@
}
mPendingIdleUntil = a;
- mConstants.updateAllowWhileIdleMinTimeLocked();
needRebatch = true;
} else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) {
@@ -1751,6 +1744,15 @@
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("]");
+
if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) {
pw.println();
pw.println(" Idle mode state:");
@@ -1803,9 +1805,6 @@
pw.println();
}
- pw.print(" mAllowWhileIdleMinTime=");
- TimeUtils.formatDuration(mAllowWhileIdleMinTime, pw);
- pw.println();
if (mLastAllowWhileIdleDispatch.size() > 0) {
pw.println(" Last allow while idle dispatch times:");
for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) {
@@ -2072,8 +2071,6 @@
f.writeToProto(proto, AlarmManagerServiceProto.OUTSTANDING_DELIVERIES);
}
- proto.write(AlarmManagerServiceProto.ALLOW_WHILE_IDLE_MIN_DURATION_MS,
- mAllowWhileIdleMinTime);
for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); ++i) {
final long token = proto.start(
AlarmManagerServiceProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
@@ -2739,6 +2736,7 @@
}
private boolean isBackgroundRestricted(Alarm alarm) {
+ final boolean allowWhileIdle = (alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0;
if (alarm.alarmClock != null) {
// Don't block alarm clocks
return false;
@@ -2751,7 +2749,8 @@
final String sourcePackage =
(alarm.operation != null) ? alarm.operation.getCreatorPackage() : alarm.packageName;
final int sourceUid = alarm.creatorUid;
- return mForceAppStandbyTracker.areAlarmsRestricted(sourceUid, sourcePackage);
+ return mForceAppStandbyTracker.areAlarmsRestricted(sourceUid, sourcePackage,
+ allowWhileIdle);
}
private native long init();
@@ -2785,8 +2784,21 @@
if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
// If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can
// schedule such alarms.
- long lastTime = mLastAllowWhileIdleDispatch.get(alarm.uid, 0);
- long minTime = lastTime + mAllowWhileIdleMinTime;
+ 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;
+ }
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
@@ -3526,6 +3538,7 @@
@Override public void onUidGone(int uid, boolean disabled) {
synchronized (mLock) {
+ mUseAllowWhileIdleShortTime.delete(uid);
if (disabled) {
removeForStoppedLocked(uid);
}
@@ -3533,6 +3546,9 @@
}
@Override public void onUidActive(int uid) {
+ synchronized (mLock) {
+ mUseAllowWhileIdleShortTime.put(uid, true);
+ }
}
@Override public void onUidIdle(int uid, boolean disabled) {
@@ -3547,7 +3563,6 @@
}
};
-
private final Listener mForceAppStandbyListener = new Listener() {
@Override
public void unblockAllUnrestrictedAlarms() {
@@ -3829,7 +3844,12 @@
if (allowWhileIdle) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
- mLastAllowWhileIdleDispatch.put(alarm.uid, nowELAPSED);
+ mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
+ if (mForceAppStandbyTracker.isInForeground(alarm.creatorUid)) {
+ mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true);
+ } else {
+ mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false);
+ }
if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = alarm.uid;
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 792fdfe..257845e 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -214,8 +214,11 @@
int uid, @NonNull String packageName) {
updateJobsForUidPackage(uid, packageName);
- if (!sender.areAlarmsRestricted(uid, packageName)) {
+ if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ false)) {
unblockAlarmsForUidPackage(uid, packageName);
+ } else if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ true)){
+ // we need to deliver the allow-while-idle alarms for this uid, package
+ unblockAllUnrestrictedAlarms();
}
}
@@ -706,7 +709,7 @@
synchronized (mLock) {
unblockAlarms = !mForcedAppStandbyEnabled && !mForceAllAppsStandby;
}
- for (Listener l: cloneListeners()) {
+ for (Listener l : cloneListeners()) {
l.updateAllJobs();
if (unblockAlarms) {
l.unblockAllUnrestrictedAlarms();
@@ -818,9 +821,10 @@
/**
* @return whether alarms should be restricted for a UID package-name.
*/
- public boolean areAlarmsRestricted(int uid, @NonNull String packageName) {
+ public boolean areAlarmsRestricted(int uid, @NonNull String packageName,
+ boolean allowWhileIdle) {
return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false,
- /* exemptOnBatterySaver =*/ false);
+ /* exemptOnBatterySaver =*/ allowWhileIdle);
}
/**
@@ -879,7 +883,6 @@
/**
* @return whether force all apps standby is enabled or not.
*
- * Note clients normally shouldn't need to access it.
*/
boolean isForceAllAppsStandbyEnabled() {
synchronized (mLock) {