Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d736276 authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

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
parent f9139ded
Loading
Loading
Loading
Loading
+31 −4
Original line number Original line Diff line number Diff line
@@ -208,10 +208,10 @@ public class AlarmManager {
    public static final int FLAG_PRIORITIZE = 1 << 6;
    public static final int FLAG_PRIORITIZE = 1 << 6;


    /**
    /**
     * For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
     * For apps targeting {@link Build.VERSION_CODES#S} or above, any APIs setting exact alarms,
     * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
     * e.g. {@link #setExact(int, long, PendingIntent)},
     * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new
     * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} and others will require holding a new
     * permission {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM}
     * permission {@link Manifest.permission#SCHEDULE_EXACT_ALARM}
     *
     *
     * @hide
     * @hide
     */
     */
@@ -219,6 +219,21 @@ public class AlarmManager {
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
    public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L;
    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
    @UnsupportedAppUsage
    private final IAlarmManager mService;
    private final IAlarmManager mService;
    private final Context mContext;
    private final Context mContext;
@@ -483,6 +498,11 @@ public class AlarmManager {
     * modest timeliness requirements for its alarms.
     * modest timeliness requirements for its alarms.
     *
     *
     * <p>
     * <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
     * This method can also be used to achieve strict ordering guarantees among
     * multiple alarms by ensuring that the windows requested for each alarm do
     * multiple alarms by ensuring that the windows requested for each alarm do
     * not intersect.
     * not intersect.
@@ -532,6 +552,13 @@ public class AlarmManager {
     * The OnAlarmListener {@link OnAlarmListener#onAlarm() onAlarm()} method will be
     * The OnAlarmListener {@link OnAlarmListener#onAlarm() onAlarm()} method will be
     * invoked via the specified target Handler, or on the application's main looper
     * invoked via the specified target Handler, or on the application's main looper
     * if {@code null} is passed as the {@code targetHandler} parameter.
     * 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,
    public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
            String tag, OnAlarmListener listener, Handler targetHandler) {
            String tag, OnAlarmListener listener, Handler targetHandler) {
+17 −7
Original line number Original line Diff line number Diff line
@@ -452,7 +452,8 @@ public class AlarmManagerService extends SystemService {
        private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
        private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
        private static final long DEFAULT_MIN_INTERVAL = 60 * 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_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_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000;
        private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
        private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
        private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
        private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
@@ -1688,12 +1689,22 @@ public class AlarmManagerService extends SystemService {
            windowLength = AlarmManager.WINDOW_EXACT;
            windowLength = AlarmManager.WINDOW_EXACT;
        }
        }


        // Sanity check the window length.  This will catch people mistakenly
        // Snap the window to reasonable limits.
        // trying to pass an end-of-window timestamp rather than a duration.
        if (windowLength > INTERVAL_DAY) {
        if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
            Slog.w(TAG, "Window length " + windowLength
            Slog.w(TAG, "Window length " + windowLength
                    + "ms suspiciously long; limiting to 1 hour");
                    + "ms suspiciously long; limiting to 1 day");
            windowLength = AlarmManager.INTERVAL_HOUR;
            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
        // 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.
            // Fix this window in place, so that as time approaches we don't collapse it.
            windowLength = maxElapsed - triggerElapsed;
            windowLength = maxElapsed - triggerElapsed;
        } else {
        } else {
            windowLength = Math.max(windowLength, mConstants.MIN_WINDOW);
            maxElapsed = triggerElapsed + windowLength;
            maxElapsed = triggerElapsed + windowLength;
        }
        }
        synchronized (mLock) {
        synchronized (mLock) {
+25 −1
Original line number Original line Diff line number Diff line
@@ -2223,7 +2223,11 @@ public class AlarmManagerServiceTest {
    }
    }


    @Test
    @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;
        final long minWindow = 73;
        setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
        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
    @Test
    public void denyListPackagesAdded() {
    public void denyListPackagesAdded() {
        mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"});
        mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"});