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 31da2017bdefc..03d9a968f7905 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -1684,17 +1684,13 @@ public class AlarmManagerService extends SystemService { } } - if ((flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) { - // Do not support windows for idle-until alarms. - windowLength = AlarmManager.WINDOW_EXACT; - } - // Snap the window to reasonable limits. if (windowLength > INTERVAL_DAY) { Slog.w(TAG, "Window length " + windowLength + "ms suspiciously long; limiting to 1 day"); windowLength = INTERVAL_DAY; - } else if (windowLength > 0 && windowLength < mConstants.MIN_WINDOW) { + } else if (windowLength > 0 && windowLength < mConstants.MIN_WINDOW + && (flags & FLAG_PRIORITIZE) == 0) { if (CompatChanges.isChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, callingPackage, UserHandle.getUserHandleForUid(callingUid))) { Slog.w(TAG, "Window length " + windowLength + "ms too short; expanding to " @@ -1741,7 +1737,7 @@ public class AlarmManagerService extends SystemService { final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger; final long maxElapsed; - if (windowLength == AlarmManager.WINDOW_EXACT) { + if (windowLength == 0) { maxElapsed = triggerElapsed; } else if (windowLength < 0) { maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval); @@ -2145,17 +2141,63 @@ public class AlarmManagerService extends SystemService { + " does not belong to the calling uid " + callingUid); } - final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0; - final boolean exact = (windowLength == AlarmManager.WINDOW_EXACT); + // Repeating alarms must use PendingIntent, not direct listener + if (interval != 0 && directReceiver != null) { + throw new IllegalArgumentException("Repeating alarms cannot use AlarmReceivers"); + } - // make sure the caller is allowed to use the requested kind of alarm, and also + if (workSource != null) { + getContext().enforcePermission( + android.Manifest.permission.UPDATE_DEVICE_STATS, + Binder.getCallingPid(), callingUid, "AlarmManager.set"); + } + + if ((flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) { + // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm + // manager when to come out of idle mode, which is only for DeviceIdleController. + if (callingUid != Process.SYSTEM_UID) { + // TODO (b/169463012): Throw instead of tolerating this mistake. + flags &= ~AlarmManager.FLAG_IDLE_UNTIL; + } else { + // Do not support windows for idle-until alarms. + windowLength = 0; + } + } + + // Remove flags reserved for the service, we will apply those later as appropriate. + flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED + | FLAG_ALLOW_WHILE_IDLE_COMPAT); + + // If this alarm is for an alarm clock, then it must be exact and we will + // use it to wake early from idle if needed. + if (alarmClock != null) { + flags |= FLAG_WAKE_FROM_IDLE; + windowLength = 0; + + // If the caller is a core system component or on the user's allowlist, and not calling + // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED. + // This means we will allow these alarms to go off as normal even while idle, with no + // timing restrictions. + } else if (workSource == null && (UserHandle.isCore(callingUid) + || UserHandle.isSameApp(callingUid, mSystemUiUid) + || ((mAppStateTracker != null) + && mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) { + flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; + flags &= ~(FLAG_ALLOW_WHILE_IDLE | FLAG_PRIORITIZE); + } + + final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0; + final boolean exact = (windowLength == 0); + + // Make sure the caller is allowed to use the requested kind of alarm, and also // decide what quota and broadcast options to use. Bundle idleOptions = null; if ((flags & FLAG_PRIORITIZE) != 0) { getContext().enforcePermission( Manifest.permission.SCHEDULE_PRIORITIZED_ALARM, Binder.getCallingPid(), callingUid, "AlarmManager.setPrioritized"); - flags &= ~(FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_COMPAT); + // The API doesn't allow using both together. + flags &= ~FLAG_ALLOW_WHILE_IDLE; } else if (exact || allowWhileIdle) { final boolean needsPermission; boolean lowerQuota; @@ -2193,55 +2235,11 @@ public class AlarmManagerService extends SystemService { } } - // Repeating alarms must use PendingIntent, not direct listener - if (interval != 0) { - if (directReceiver != null) { - throw new IllegalArgumentException( - "Repeating alarms cannot use AlarmReceivers"); - } - } - - if (workSource != null) { - getContext().enforcePermission( - android.Manifest.permission.UPDATE_DEVICE_STATS, - Binder.getCallingPid(), callingUid, "AlarmManager.set"); - } - - // No incoming callers can request either WAKE_FROM_IDLE or - // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate. - flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED); - - // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm - // manager when to come out of idle mode, which is only for DeviceIdleController. - if (callingUid != Process.SYSTEM_UID) { - flags &= ~AlarmManager.FLAG_IDLE_UNTIL; - } - // If this is an exact time alarm, then it can't be batched with other alarms. - if (windowLength == AlarmManager.WINDOW_EXACT) { + if (exact) { flags |= AlarmManager.FLAG_STANDALONE; } - // If this alarm is for an alarm clock, then it must be standalone and we will - // use it to wake early from idle if needed. - if (alarmClock != null) { - flags |= FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; - - // If the caller is a core system component or on the user's whitelist, and not calling - // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED. - // This means we will allow these alarms to go off as normal even while idle, with no - // timing restrictions. - } else if (workSource == null && (UserHandle.isCore(callingUid) - || UserHandle.isSameApp(callingUid, mSystemUiUid) - || ((mAppStateTracker != null) - && mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) { - flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; - flags &= ~FLAG_ALLOW_WHILE_IDLE; - flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT; - flags &= ~FLAG_PRIORITIZE; - idleOptions = null; - } - setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver, listenerTag, flags, workSource, alarmClock, callingUid, callingPackage, idleOptions); 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 c37d2c36b0685..9b1b06658a988 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java @@ -16,7 +16,6 @@ package com.android.server.alarm; -import static com.android.server.alarm.AlarmManagerService.TAG; import static com.android.server.alarm.AlarmManagerService.dumpAlarmList; import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm; 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 7654093f9e7ec..d55bbd1d8e45c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -468,8 +468,9 @@ public class AlarmManagerServiceTest { TEST_CALLING_UID); } - private void setPrioritizedAlarm(int type, long triggerTime, IAlarmListener listener) { - mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test", + private void setPrioritizedAlarm(int type, long triggerTime, long windowLength, + IAlarmListener listener) { + mService.setImpl(type, triggerTime, windowLength, 0, null, listener, "test", FLAG_STANDALONE | FLAG_PRIORITIZE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE, null); } @@ -1685,7 +1686,7 @@ public class AlarmManagerServiceTest { final int numAlarms = 10; for (int i = 0; i < numAlarms; i++) { setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i, - new IAlarmListener.Stub() { + 0, new IAlarmListener.Stub() { @Override public void doAlarm(IAlarmCompleteListener callback) throws RemoteException { @@ -1720,7 +1721,7 @@ public class AlarmManagerServiceTest { final int numAlarms = 10; for (int i = 0; i < numAlarms; i++) { setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i, - new IAlarmListener.Stub() { + 0, new IAlarmListener.Stub() { @Override public void doAlarm(IAlarmCompleteListener callback) throws RemoteException { @@ -1738,12 +1739,12 @@ public class AlarmManagerServiceTest { } assertEquals(numAlarms, alarmsFired.get()); - setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 3, new IAlarmListener.Stub() { + setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 3, 0, new IAlarmListener.Stub() { @Override public void doAlarm(IAlarmCompleteListener callback) throws RemoteException { } }); - setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 2, new IAlarmListener.Stub() { + setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 2, 0, new IAlarmListener.Stub() { @Override public void doAlarm(IAlarmCompleteListener callback) throws RemoteException { } @@ -2262,6 +2263,28 @@ public class AlarmManagerServiceTest { } } + @Test + public void minWindowPriorityAlarm() { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled( + eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS), + anyString(), any(UserHandle.class))); + final long minWindow = 73; + setDeviceConfigLong(KEY_MIN_WINDOW, minWindow); + + // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC. + for (int window = 1; window <= minWindow; window++) { + setPrioritizedAlarm(ELAPSED_REALTIME, 0, window, new IAlarmListener.Stub() { + @Override + public void doAlarm(IAlarmCompleteListener callback) throws RemoteException { + } + }); + assertEquals(1, mService.mAlarmStore.size()); + final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0); + assertEquals(window, a.windowLength); + } + } + @Test public void denyListPackagesAdded() { mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"});