Merge "Enforcing a minimum window on inexact alarms" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-04-15 03:10:37 +00:00
committed by Android (Google) Code Review
3 changed files with 73 additions and 12 deletions

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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"});