Enforcing a minimum window on inexact alarms
Any non-exact alarms with window > 0, should have a specific minimum window length. Otherwise, very small windows, say 1ms, are pratically indistinguishable from exact alarms. Test: atest FrameworksMockingServicesTests:com.android.server.alarm Bug: 185199076 Bug: 185199018 Change-Id: Idf933b70a66f2a749cdb2689ab68a80204d212b1
This commit is contained in:
@@ -208,10 +208,10 @@ public class AlarmManager {
|
||||
public static final int FLAG_PRIORITIZE = 1 << 6;
|
||||
|
||||
/**
|
||||
* For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
|
||||
* {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
|
||||
* {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new
|
||||
* permission {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM}
|
||||
* For apps targeting {@link Build.VERSION_CODES#S} or above, any APIs setting exact alarms,
|
||||
* e.g. {@link #setExact(int, long, PendingIntent)},
|
||||
* {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} and others will require holding a new
|
||||
* permission {@link Manifest.permission#SCHEDULE_EXACT_ALARM}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@@ -219,6 +219,21 @@ public class AlarmManager {
|
||||
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
|
||||
public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L;
|
||||
|
||||
/**
|
||||
* For apps targeting {@link Build.VERSION_CODES#S} or above, all inexact alarms will require
|
||||
* to have a minimum window size, expected to be on the order of a few minutes.
|
||||
*
|
||||
* Practically, any alarms requiring smaller windows are the same as exact alarms and should use
|
||||
* the corresponding APIs provided, like {@link #setExact(int, long, PendingIntent)}, et al.
|
||||
*
|
||||
* Inexact alarm with shorter windows specified will have their windows elongated by the system.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@ChangeId
|
||||
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
|
||||
public static final long ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS = 185199076L;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
private final IAlarmManager mService;
|
||||
private final Context mContext;
|
||||
@@ -483,6 +498,11 @@ public class AlarmManager {
|
||||
* modest timeliness requirements for its alarms.
|
||||
*
|
||||
* <p>
|
||||
* Note: Starting with API {@link Build.VERSION_CODES#S}, the system will ensure that the window
|
||||
* specified is at least a few minutes, as smaller windows are considered practically exact
|
||||
* and should use the other APIs provided for exact alarms.
|
||||
*
|
||||
* <p>
|
||||
* This method can also be used to achieve strict ordering guarantees among
|
||||
* multiple alarms by ensuring that the windows requested for each alarm do
|
||||
* not intersect.
|
||||
@@ -532,6 +552,13 @@ public class AlarmManager {
|
||||
* The OnAlarmListener {@link OnAlarmListener#onAlarm() onAlarm()} method will be
|
||||
* invoked via the specified target Handler, or on the application's main looper
|
||||
* if {@code null} is passed as the {@code targetHandler} parameter.
|
||||
*
|
||||
* <p>
|
||||
* Note: Starting with API {@link Build.VERSION_CODES#S}, the system will ensure that the window
|
||||
* specified is at least a few minutes, as smaller windows are considered practically exact
|
||||
* and should use the other APIs provided for exact alarms.
|
||||
*
|
||||
* @see #setWindow(int, long, long, PendingIntent)
|
||||
*/
|
||||
public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
|
||||
String tag, OnAlarmListener listener, Handler targetHandler) {
|
||||
|
||||
@@ -452,7 +452,8 @@ public class AlarmManagerService extends SystemService {
|
||||
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
|
||||
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
|
||||
private static final long DEFAULT_MAX_INTERVAL = 365 * INTERVAL_DAY;
|
||||
private static final long DEFAULT_MIN_WINDOW = 10_000;
|
||||
// TODO (b/185199076): Tune based on breakage reports.
|
||||
private static final long DEFAULT_MIN_WINDOW = 30 * 60 * 1000;
|
||||
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000;
|
||||
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
|
||||
private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
|
||||
@@ -1688,12 +1689,22 @@ public class AlarmManagerService extends SystemService {
|
||||
windowLength = AlarmManager.WINDOW_EXACT;
|
||||
}
|
||||
|
||||
// Sanity check the window length. This will catch people mistakenly
|
||||
// trying to pass an end-of-window timestamp rather than a duration.
|
||||
if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
|
||||
// Snap the window to reasonable limits.
|
||||
if (windowLength > INTERVAL_DAY) {
|
||||
Slog.w(TAG, "Window length " + windowLength
|
||||
+ "ms suspiciously long; limiting to 1 hour");
|
||||
windowLength = AlarmManager.INTERVAL_HOUR;
|
||||
+ "ms suspiciously long; limiting to 1 day");
|
||||
windowLength = INTERVAL_DAY;
|
||||
} else if (windowLength > 0 && windowLength < mConstants.MIN_WINDOW) {
|
||||
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 "
|
||||
+ mConstants.MIN_WINDOW + "ms.");
|
||||
windowLength = mConstants.MIN_WINDOW;
|
||||
} else {
|
||||
// TODO (b/185199076): Remove log once we have some data about what apps will break
|
||||
Slog.wtf(TAG, "Short window " + windowLength + "ms specified by "
|
||||
+ callingPackage);
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check the recurrence interval. This will catch people who supply
|
||||
@@ -1737,7 +1748,6 @@ public class AlarmManagerService extends SystemService {
|
||||
// Fix this window in place, so that as time approaches we don't collapse it.
|
||||
windowLength = maxElapsed - triggerElapsed;
|
||||
} else {
|
||||
windowLength = Math.max(windowLength, mConstants.MIN_WINDOW);
|
||||
maxElapsed = triggerElapsed + windowLength;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
|
||||
@@ -2223,7 +2223,11 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void minWindow() {
|
||||
public void minWindowChangeEnabled() {
|
||||
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);
|
||||
|
||||
@@ -2238,6 +2242,26 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void minWindowChangeDisabled() {
|
||||
doReturn(false).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++) {
|
||||
final PendingIntent pi = getNewMockPendingIntent();
|
||||
setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null);
|
||||
|
||||
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"});
|
||||
|
||||
Reference in New Issue
Block a user