diff options
| -rw-r--r-- | apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java | 132 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/src/com/android/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 0e362759ef4c..64686a1682f0 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 volatile Set<String> 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 Set<String> EXACT_ALARM_DENY_LIST = Collections.emptySet(); + 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 64dad7f0feab..8382ff4cb506 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 @@ -737,6 +743,20 @@ public class AlarmManagerServiceTest { } @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); assertEquals(10, mService.mConstants.MIN_FUTURITY); @@ -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(); |