From 74b85211fa12e3e0585fb538f313c234c3e0c27b Mon Sep 17 00:00:00 2001 From: Suprabh Shukla Date: Mon, 3 May 2021 19:46:34 -0700 Subject: [PATCH 1/2] Hold wakeup alarms for as long as possible If the screen is off, but the CPU is awake and epoll_wait returns for a non-wakeup alarm, sending any wakeup alarm within its eligibility window would cause all the held non-wakeup alarms to also fire, which could: 1. Elongate the length of the time the CPU is awake. 2. Cause apps to wake up and perform work. This change defers sending any wakeup alarm as long as it is possible to defer all such eligible alarms to later. Also, fixed a bug in sorting after adding multiple alarms to the store. Test: atest CtsAlarmManagerTestCases atest FrameworksMockingServicesTests:com.android.server.alarm Bug: 161497385 Change-Id: I01f98255091baddf4e7529f88733776e27d090d7 --- .../android/server/alarm/LazyAlarmStore.java | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java index 2e12e2f34ea07..0073335a13320 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java @@ -40,6 +40,7 @@ import java.util.function.Predicate; public class LazyAlarmStore implements AlarmStore { @VisibleForTesting static final String TAG = LazyAlarmStore.class.getSimpleName(); + private static final long ALARM_DEADLINE_SLOP = 500; private final ArrayList mAlarms = new ArrayList<>(); private Runnable mOnAlarmClockRemoved; @@ -75,7 +76,7 @@ public class LazyAlarmStore implements AlarmStore { return; } mAlarms.addAll(alarms); - Collections.sort(alarms, sDecreasingTimeOrder); + Collections.sort(mAlarms, sDecreasingTimeOrder); } @Override @@ -163,25 +164,47 @@ public class LazyAlarmStore implements AlarmStore { @Override public ArrayList removePendingAlarms(long nowElapsed) { final ArrayList pending = new ArrayList<>(); - final ArrayList standAlones = new ArrayList<>(); + + // Only send wake-up alarms if this is the absolutely latest time we can evaluate + // for at least one wakeup alarm. This prevents sending other non-wakeup alarms when the + // screen is off but the CPU is awake for some reason. + boolean sendWakeups = false; + + // If any alarm with FLAG_STANDALONE is present, we cannot send any alarms without that flag + // in the present batch. + boolean standalonesOnly = false; for (int i = mAlarms.size() - 1; i >= 0; i--) { final Alarm alarm = mAlarms.get(i); if (alarm.getWhenElapsed() > nowElapsed) { break; } + mAlarms.remove(i); pending.add(alarm); + if (alarm.wakeup && alarm.getMaxWhenElapsed() <= nowElapsed + ALARM_DEADLINE_SLOP) { + // Using some slop as it is better to send the wakeup alarm now, rather than + // waking up again a short time later, just to send it. + sendWakeups = true; + } if ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) { - standAlones.add(alarm); + standalonesOnly = true; } } - if (!standAlones.isEmpty()) { - // If there are deliverable standalone alarms, others must not go out yet. - mAlarms.removeAll(standAlones); - return standAlones; + final ArrayList toSend = new ArrayList<>(); + for (int i = pending.size() - 1; i >= 0; i--) { + final Alarm pendingAlarm = pending.get(i); + if (!sendWakeups && pendingAlarm.wakeup) { + continue; + } + if (standalonesOnly && (pendingAlarm.flags & AlarmManager.FLAG_STANDALONE) == 0) { + continue; + } + pending.remove(i); + toSend.add(pendingAlarm); } - mAlarms.removeAll(pending); - return pending; + // Perhaps some alarms could not be sent right now. Adding them back for later. + addAll(pending); + return toSend; } @Override From faa2caf0ee9ae6cc0e80475700fbcfafbe74dd50 Mon Sep 17 00:00:00 2001 From: Suprabh Shukla Date: Fri, 30 Apr 2021 00:14:43 -0700 Subject: [PATCH 2/2] Change device-idle alarm fuzzing - Removing fuzz in the absence of an alarm clock alarm. - Adding a minimum duration that the alarm should be fuzzed before an alarm clock - currently 2 minutes. - The maximum fuzz is now a linear function of futurity of the alarm with the maximum capped at 15 minutes. - If the futurity is less than the minimum fuzz, the alarm goes off immediately, bringing the device out of idle. Also, fixing a bug with re-evaluating alarm deliverability in doze and battery saver. Test: atest FrameworksMockingServicesTests:com.android.server.alarm Bug: 170279979 Bug: 166693547 Change-Id: Ia8c656a0336cc0b72f0b3901de96d4aa440186d4 --- .../server/alarm/AlarmManagerService.java | 132 +++++++++++------- .../server/alarm/AlarmManagerServiceTest.java | 79 ++++++++--- 2 files changed, 144 insertions(+), 67 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 0e362759ef4cc..64686a1682f0d 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -151,10 +151,10 @@ import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Locale; -import java.util.Random; import java.util.Set; import java.util.TimeZone; import java.util.TreeSet; +import java.util.concurrent.ThreadLocalRandom; import java.util.function.Predicate; /** @@ -251,7 +251,6 @@ public class AlarmManagerService extends SystemService { Intent mTimeTickIntent; IAlarmListener mTimeTickTrigger; PendingIntent mDateChangeSender; - Random mRandom; boolean mInteractive = true; long mNonInteractiveStartTime; long mNonInteractiveTime; @@ -516,6 +515,10 @@ public class AlarmManagerService extends SystemService { static final String KEY_PRIORITY_ALARM_DELAY = "priority_alarm_delay"; @VisibleForTesting static final String KEY_EXACT_ALARM_DENY_LIST = "exact_alarm_deny_list"; + @VisibleForTesting + static final String KEY_MIN_DEVICE_IDLE_FUZZ = "min_device_idle_fuzz"; + @VisibleForTesting + static final String KEY_MAX_DEVICE_IDLE_FUZZ = "max_device_idle_fuzz"; private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; @@ -556,6 +559,9 @@ public class AlarmManagerService extends SystemService { private static final long DEFAULT_PRIORITY_ALARM_DELAY = 9 * 60_000; + private static final long DEFAULT_MIN_DEVICE_IDLE_FUZZ = 2 * 60_000; + private static final long DEFAULT_MAX_DEVICE_IDLE_FUZZ = 15 * 60_000; + // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; @@ -618,10 +624,23 @@ public class AlarmManagerService extends SystemService { public long PRIORITY_ALARM_DELAY = DEFAULT_PRIORITY_ALARM_DELAY; /** - * Set of apps that won't get SCHEDULE_EXACT_ALARM when the app-op mode for - * OP_SCHEDULE_EXACT_ALARM is MODE_DEFAULT. + * Read-only set of apps that won't get SCHEDULE_EXACT_ALARM when the app-op mode for + * OP_SCHEDULE_EXACT_ALARM is MODE_DEFAULT. Since this is read-only and volatile, this can + * be accessed without synchronizing on {@link #mLock}. */ - public Set EXACT_ALARM_DENY_LIST = Collections.emptySet(); + public volatile Set EXACT_ALARM_DENY_LIST = Collections.emptySet(); + + /** + * Minimum time interval that an IDLE_UNTIL will be pulled earlier to a subsequent + * WAKE_FROM_IDLE alarm. + */ + public long MIN_DEVICE_IDLE_FUZZ = DEFAULT_MIN_DEVICE_IDLE_FUZZ; + + /** + * Maximum time interval that an IDLE_UNTIL will be pulled earlier to a subsequent + * WAKE_FROM_IDLE alarm. + */ + public long MAX_DEVICE_IDLE_FUZZ = DEFAULT_MAX_DEVICE_IDLE_FUZZ; private long mLastAllowWhileIdleWhitelistDuration = -1; private int mVersion = 0; @@ -660,6 +679,7 @@ public class AlarmManagerService extends SystemService { @Override public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { boolean standbyQuotaUpdated = false; + boolean deviceIdleFuzzBoundariesUpdated = false; synchronized (mLock) { mVersion++; for (String name : properties.getKeyset()) { @@ -787,6 +807,13 @@ public class AlarmManagerService extends SystemService { updateExactAlarmDenyList(values); } break; + case KEY_MIN_DEVICE_IDLE_FUZZ: + case KEY_MAX_DEVICE_IDLE_FUZZ: + if (!deviceIdleFuzzBoundariesUpdated) { + updateDeviceIdleFuzzBoundaries(); + deviceIdleFuzzBoundariesUpdated = true; + } + break; default: if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { // The quotas need to be updated in order, so we can't just rely @@ -824,6 +851,24 @@ public class AlarmManagerService extends SystemService { mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater); } + private void updateDeviceIdleFuzzBoundaries() { + final DeviceConfig.Properties properties = DeviceConfig.getProperties( + DeviceConfig.NAMESPACE_ALARM_MANAGER, + KEY_MIN_DEVICE_IDLE_FUZZ, KEY_MAX_DEVICE_IDLE_FUZZ); + + MIN_DEVICE_IDLE_FUZZ = properties.getLong(KEY_MIN_DEVICE_IDLE_FUZZ, + DEFAULT_MIN_DEVICE_IDLE_FUZZ); + MAX_DEVICE_IDLE_FUZZ = properties.getLong(KEY_MAX_DEVICE_IDLE_FUZZ, + DEFAULT_MAX_DEVICE_IDLE_FUZZ); + + if (MAX_DEVICE_IDLE_FUZZ < MIN_DEVICE_IDLE_FUZZ) { + Slog.w(TAG, "max_device_idle_fuzz cannot be smaller than" + + " min_device_idle_fuzz! Increasing to " + + MIN_DEVICE_IDLE_FUZZ); + MAX_DEVICE_IDLE_FUZZ = MIN_DEVICE_IDLE_FUZZ; + } + } + private void updateStandbyQuotasLocked() { // The bucket quotas need to be read as an atomic unit but the properties passed to // onPropertiesChanged may only have one key populated at a time. @@ -1133,12 +1178,8 @@ public class AlarmManagerService extends SystemService { if (mNextWakeFromIdle != null && isRtc(mNextWakeFromIdle.type)) { // The next wake from idle got updated due to the rtc time change, so we need // to update the time we have to come out of idle too. - final boolean idleUntilUpdated = mAlarmStore.updateAlarmDeliveries(a -> { - if (a != mPendingIdleUntil) { - return false; - } - return adjustIdleUntilTime(a); - }); + final boolean idleUntilUpdated = mAlarmStore.updateAlarmDeliveries( + a -> (a == mPendingIdleUntil) && adjustIdleUntilTime(a)); if (idleUntilUpdated) { mAlarmStore.updateAlarmDeliveries( alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); @@ -1911,23 +1952,30 @@ public class AlarmManagerService extends SystemService { if ((alarm.flags & AlarmManager.FLAG_IDLE_UNTIL) == 0) { return false; } - restoreRequestedTime(alarm); - long triggerBeforeFuzz = alarm.getRequestedElapsed(); - if (mNextWakeFromIdle != null && triggerBeforeFuzz > mNextWakeFromIdle.getWhenElapsed()) { - triggerBeforeFuzz = mNextWakeFromIdle.getWhenElapsed(); + final boolean changedBeforeFuzz = restoreRequestedTime(alarm); + if (mNextWakeFromIdle == null) { + // No need to change anything in the absence of a wake-from-idle request. + return changedBeforeFuzz; } - // Add fuzz to make the alarm go off some time before the actual desired time. - final int fuzz = fuzzForDuration(alarm.getWhenElapsed() - mInjector.getElapsedRealtime()); - final int delta; - if (fuzz > 0) { - if (mRandom == null) { - mRandom = new Random(); - } - delta = mRandom.nextInt(fuzz); + final long upcomingWakeFromIdle = mNextWakeFromIdle.getWhenElapsed(); + // Add fuzz to make the alarm go off some time before the next upcoming wake-from-idle, as + // these alarms are usually wall-clock aligned. + if (alarm.getWhenElapsed() < (upcomingWakeFromIdle - mConstants.MIN_DEVICE_IDLE_FUZZ)) { + // No need to fuzz as this is already earlier than the coming wake-from-idle. + return changedBeforeFuzz; + } + final long nowElapsed = mInjector.getElapsedRealtime(); + final long futurity = upcomingWakeFromIdle - nowElapsed; + + if (futurity <= mConstants.MIN_DEVICE_IDLE_FUZZ) { + // No point in fuzzing as the minimum fuzz will take the time in the past. + alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, nowElapsed); } else { - delta = 0; + final ThreadLocalRandom random = ThreadLocalRandom.current(); + final long upperBoundExcl = Math.min(mConstants.MAX_DEVICE_IDLE_FUZZ, futurity) + 1; + final long fuzz = random.nextLong(mConstants.MIN_DEVICE_IDLE_FUZZ, upperBoundExcl); + alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, upcomingWakeFromIdle - fuzz); } - alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, triggerBeforeFuzz - delta); return true; } @@ -2130,12 +2178,8 @@ public class AlarmManagerService extends SystemService { // If this wake from idle is earlier than whatever was previously scheduled, // and we are currently idling, then the idle-until time needs to be updated. if (mPendingIdleUntil != null) { - final boolean updated = mAlarmStore.updateAlarmDeliveries(alarm -> { - if (alarm != mPendingIdleUntil) { - return false; - } - return adjustIdleUntilTime(alarm); - }); + final boolean updated = mAlarmStore.updateAlarmDeliveries( + alarm -> (alarm == mPendingIdleUntil) && adjustIdleUntilTime(alarm)); if (updated) { // idle-until got updated, so also update all alarms not allowed while idle. mAlarmStore.updateAlarmDeliveries( @@ -3675,20 +3719,6 @@ public class AlarmManagerService extends SystemService { } } - int fuzzForDuration(long duration) { - if (duration < 15 * 60 * 1000) { - // If the duration until the time is less than 15 minutes, the maximum fuzz - // is the duration. - return (int) duration; - } else if (duration < 90 * 60 * 1000) { - // If duration is less than 1 1/2 hours, the maximum fuzz is 15 minutes, - return 15 * 60 * 1000; - } else { - // Otherwise, we will fuzz by at most half an hour. - return 30 * 60 * 1000; - } - } - boolean checkAllowNonWakeupDelayLocked(long nowELAPSED) { if (mInteractive) { return false; @@ -4698,8 +4728,10 @@ public class AlarmManagerService extends SystemService { if (a.creatorUid != alarm.creatorUid || !isAllowedWhileIdleRestricted(a)) { return false; } - return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a)) - || (batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a)); + final boolean dozeAdjusted = doze && adjustDeliveryTimeBasedOnDeviceIdle(a); + final boolean batterySaverAdjusted = + batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a); + return dozeAdjusted || batterySaverAdjusted; }); } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) { mLastPriorityAlarmDispatch.put(alarm.creatorUid, nowELAPSED); @@ -4708,8 +4740,10 @@ public class AlarmManagerService extends SystemService { || (alarm.flags & FLAG_PRIORITIZE) == 0) { return false; } - return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a)) - || (batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a)); + final boolean dozeAdjusted = doze && adjustDeliveryTimeBasedOnDeviceIdle(a); + final boolean batterySaverAdjusted = + batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a); + return dozeAdjusted || batterySaverAdjusted; }); } if (RECORD_DEVICE_IDLE_ALARMS) { diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 64dad7f0feab7..8382ff4cb5063 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -71,7 +71,9 @@ import static com.android.server.alarm.AlarmManagerService.Constants.KEY_CRASH_N import static com.android.server.alarm.AlarmManagerService.Constants.KEY_EXACT_ALARM_DENY_LIST; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT; +import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_DEVICE_IDLE_FUZZ; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL; +import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_DEVICE_IDLE_FUZZ; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_WINDOW; @@ -654,6 +656,8 @@ public class AlarmManagerServiceTest { setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 45); setDeviceConfigLong(KEY_MIN_WINDOW, 50); setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, 55); + setDeviceConfigLong(KEY_MIN_DEVICE_IDLE_FUZZ, 60); + setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 65); assertEquals(5, mService.mConstants.MIN_FUTURITY); assertEquals(10, mService.mConstants.MIN_INTERVAL); assertEquals(15, mService.mConstants.MAX_INTERVAL); @@ -665,6 +669,8 @@ public class AlarmManagerServiceTest { assertEquals(45, mService.mConstants.LISTENER_TIMEOUT); assertEquals(50, mService.mConstants.MIN_WINDOW); assertEquals(55, mService.mConstants.PRIORITY_ALARM_DELAY); + assertEquals(60, mService.mConstants.MIN_DEVICE_IDLE_FUZZ); + assertEquals(65, mService.mConstants.MAX_DEVICE_IDLE_FUZZ); } @Test @@ -736,6 +742,20 @@ public class AlarmManagerServiceTest { mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW); } + @Test + public void deviceIdleFuzzRangeNonNegative() { + final long newMinFuzz = mService.mConstants.MAX_DEVICE_IDLE_FUZZ + 1542; + final long newMaxFuzz = mService.mConstants.MIN_DEVICE_IDLE_FUZZ - 131; + + setDeviceConfigLong(KEY_MIN_DEVICE_IDLE_FUZZ, newMinFuzz); + assertTrue("Negative device-idle fuzz range", mService.mConstants.MAX_DEVICE_IDLE_FUZZ + >= mService.mConstants.MIN_DEVICE_IDLE_FUZZ); + + setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, newMaxFuzz); + assertTrue("Negative device-idle fuzz range", mService.mConstants.MAX_DEVICE_IDLE_FUZZ + >= mService.mConstants.MIN_DEVICE_IDLE_FUZZ); + } + @Test public void testMinFuturity() { setDeviceConfigLong(KEY_MIN_FUTURITY, 10L); @@ -1431,7 +1451,7 @@ public class AlarmManagerServiceTest { @Test public void singleIdleUntil() { - doReturn(0).when(mService).fuzzForDuration(anyLong()); + setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0); final PendingIntent idleUntilPi6 = getNewMockPendingIntent(); setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, idleUntilPi6); @@ -1504,44 +1524,67 @@ public class AlarmManagerServiceTest { assertNull(mService.mNextWakeFromIdle); } + private static void assertInRange(String message, long minIncl, long maxIncl, long val) { + assertTrue(message, val >= minIncl && val <= maxIncl); + } + @Test - public void idleUntilBeforeWakeFromIdle() { - doReturn(0).when(mService).fuzzForDuration(anyLong()); + public void idleUntilFuzzedBeforeWakeFromIdle() { + final long minFuzz = 6; + final long maxFuzz = 17; + setDeviceConfigLong(KEY_MIN_DEVICE_IDLE_FUZZ, minFuzz); + setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, maxFuzz); + + mNowElapsedTest = 119; // Arbitrary, just to ensure we are not testing on 0. final PendingIntent idleUntilPi = getNewMockPendingIntent(); - final long requestedIdleUntil = mNowElapsedTest + 10; + final long requestedIdleUntil = mNowElapsedTest + 12; setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, requestedIdleUntil, idleUntilPi); assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed()); final PendingIntent wakeFromIdle5 = getNewMockPendingIntent(); setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, wakeFromIdle5); - assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed()); + // Anything before now, gets snapped to now. It is not necessary for it to fire + // immediately, just how it is implemented today for simplicity. + assertEquals(mNowElapsedTest, mService.mPendingIdleUntil.getWhenElapsed()); final PendingIntent wakeFromIdle8 = getNewMockPendingIntent(); setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 8, wakeFromIdle8); - assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed()); + // Next wake from idle is still the same. + assertEquals(mNowElapsedTest, mService.mPendingIdleUntil.getWhenElapsed()); - final PendingIntent wakeFromIdle12 = getNewMockPendingIntent(); - setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 12, wakeFromIdle12); - assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed()); + final PendingIntent wakeFromIdle19 = getNewMockPendingIntent(); + setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 19, wakeFromIdle19); + // Next wake from idle is still the same. + assertEquals(mNowElapsedTest, mService.mPendingIdleUntil.getWhenElapsed()); mService.removeLocked(wakeFromIdle5, null, REMOVE_REASON_UNDEFINED); - assertEquals(mNowElapsedTest + 8, mService.mPendingIdleUntil.getWhenElapsed()); + // Next wake from idle is at now + 8. + long min = mNowElapsedTest; + long max = mNowElapsedTest + 8 - minFuzz; + assertInRange("Idle until alarm time not in expected range [" + min + ", " + max + "]", + min, max, mService.mPendingIdleUntil.getWhenElapsed()); mService.removeLocked(wakeFromIdle8, null, REMOVE_REASON_UNDEFINED); + // Next wake from idle is at now + 19, which is > minFuzz distance from + // the requested idle until time: now + 12. assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed()); mService.removeLocked(idleUntilPi, null, REMOVE_REASON_UNDEFINED); assertNull(mService.mPendingIdleUntil); - setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, idleUntilPi); - assertEquals(mNowElapsedTest + 12, mService.mPendingIdleUntil.getWhenElapsed()); + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 21, idleUntilPi); + // Next wake from idle is at now + 19, which means this alarm should get pulled back. + min = mNowElapsedTest + 19 - maxFuzz; + max = mNowElapsedTest + 19 - minFuzz; + assertInRange("Idle until alarm time not in expected range [" + min + ", " + max + "]", + min, max, mService.mPendingIdleUntil.getWhenElapsed()); } @Test public void allowWhileIdleAlarmsWhileDeviceIdle() throws Exception { - doReturn(0).when(mService).fuzzForDuration(anyLong()); + setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0); setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + mAllowWhileIdleWindow + 1000, getNewMockPendingIntent()); @@ -1571,7 +1614,7 @@ public class AlarmManagerServiceTest { @Test public void allowWhileIdleUnrestricted() throws Exception { - doReturn(0).when(mService).fuzzForDuration(anyLong()); + setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0); // Both battery saver and doze are on. setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1000, @@ -1597,7 +1640,7 @@ public class AlarmManagerServiceTest { @Test public void deviceIdleDeferralOnSet() throws Exception { - doReturn(0).when(mService).fuzzForDuration(anyLong()); + setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0); final long deviceIdleUntil = mNowElapsedTest + 1234; setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, deviceIdleUntil, getNewMockPendingIntent()); @@ -1622,7 +1665,7 @@ public class AlarmManagerServiceTest { @Test public void deviceIdleStateChanges() throws Exception { - doReturn(0).when(mService).fuzzForDuration(anyLong()); + setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0); final int numAlarms = 10; final PendingIntent[] pis = new PendingIntent[numAlarms]; @@ -1740,7 +1783,7 @@ public class AlarmManagerServiceTest { @Test public void prioritizedAlarmsInDeviceIdle() throws Exception { - doReturn(0).when(mService).fuzzForDuration(anyLong()); + setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0); final long minDelay = 5; setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, minDelay); @@ -1791,7 +1834,7 @@ public class AlarmManagerServiceTest { @Test public void dispatchOrder() throws Exception { - doReturn(0).when(mService).fuzzForDuration(anyLong()); + setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0); final long deviceIdleUntil = mNowElapsedTest + 1234; final PendingIntent idleUntilPi = getNewMockPendingIntent();