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

Commit 7c426b22 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Separating alarm history for lower quota" into sc-dev am: 977e2adc

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15264659

Change-Id: I7acd562f316859c8f31adf6037aa3da1361856b2
parents 98e82ef0 977e2adc
Loading
Loading
Loading
Loading
+26 −7
Original line number Diff line number Diff line
@@ -256,6 +256,7 @@ public class AlarmManagerService extends SystemService {
    AlarmHandler mHandler;
    AppWakeupHistory mAppWakeupHistory;
    AppWakeupHistory mAllowWhileIdleHistory;
    AppWakeupHistory mAllowWhileIdleCompatHistory;
    private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
    private final SparseArray<RingBuffer<RemovedAlarm>> mRemovalHistory = new SparseArray<>();
    ClockReceiver mClockReceiver;
@@ -1633,6 +1634,7 @@ public class AlarmManagerService extends SystemService {

            mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
            mAllowWhileIdleHistory = new AppWakeupHistory(INTERVAL_HOUR);
            mAllowWhileIdleCompatHistory = new AppWakeupHistory(INTERVAL_HOUR);

            mNextWakeup = mNextNonWakeup = 0;

@@ -2142,20 +2144,23 @@ public class AlarmManagerService extends SystemService {
            final int userId = UserHandle.getUserId(alarm.creatorUid);
            final int quota;
            final long window;
            final AppWakeupHistory history;
            if ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) {
                quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
                window = mConstants.ALLOW_WHILE_IDLE_WINDOW;
                history = mAllowWhileIdleHistory;
            } else {
                quota = mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
                window = mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
                history = mAllowWhileIdleCompatHistory;
            }
            final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
            final int dispatchesInHistory = history.getTotalWakeupsInWindow(
                    alarm.sourcePackage, userId);
            if (dispatchesInWindow < quota) {
            if (dispatchesInHistory < quota) {
                // fine to go out immediately.
                batterySaverPolicyElapsed = nowElapsed;
            } else {
                batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
                batterySaverPolicyElapsed = history.getNthLastWakeupForPackage(
                        alarm.sourcePackage, userId, quota) + window;
            }
        } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
@@ -2201,20 +2206,23 @@ public class AlarmManagerService extends SystemService {
            final int userId = UserHandle.getUserId(alarm.creatorUid);
            final int quota;
            final long window;
            final AppWakeupHistory history;
            if ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) {
                quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
                window = mConstants.ALLOW_WHILE_IDLE_WINDOW;
                history = mAllowWhileIdleHistory;
            } else {
                quota = mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
                window = mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
                history = mAllowWhileIdleCompatHistory;
            }
            final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
            final int dispatchesInHistory = history.getTotalWakeupsInWindow(
                    alarm.sourcePackage, userId);
            if (dispatchesInWindow < quota) {
            if (dispatchesInHistory < quota) {
                // fine to go out immediately.
                deviceIdlePolicyTime = nowElapsed;
            } else {
                final long whenInQuota = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
                final long whenInQuota = history.getNthLastWakeupForPackage(
                        alarm.sourcePackage, userId, quota) + window;
                deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
            }
@@ -2502,6 +2510,7 @@ public class AlarmManagerService extends SystemService {
                        Binder.getCallingPid(), callingUid, "AlarmManager.setPrioritized");
                // The API doesn't allow using both together.
                flags &= ~FLAG_ALLOW_WHILE_IDLE;
                // Prioritized alarms don't need any extra permission to be exact.
            } else if (exact || allowWhileIdle) {
                final boolean needsPermission;
                boolean lowerQuota;
@@ -2992,6 +3001,10 @@ public class AlarmManagerService extends SystemService {
            mAllowWhileIdleHistory.dump(pw, nowELAPSED);
            pw.println();

            pw.println("Allow while idle compat history:");
            mAllowWhileIdleCompatHistory.dump(pw, nowELAPSED);
            pw.println();

            if (mLastPriorityAlarmDispatch.size() > 0) {
                pw.println("Last priority alarm dispatches:");
                pw.increaseIndent();
@@ -4553,6 +4566,7 @@ public class AlarmManagerService extends SystemService {
                            removeUserLocked(userHandle);
                            mAppWakeupHistory.removeForUser(userHandle);
                            mAllowWhileIdleHistory.removeForUser(userHandle);
                            mAllowWhileIdleCompatHistory.removeForUser(userHandle);
                        }
                        return;
                    case Intent.ACTION_UID_REMOVED:
@@ -4588,6 +4602,8 @@ public class AlarmManagerService extends SystemService {
                            // package-removed and package-restarted case
                            mAppWakeupHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
                            mAllowWhileIdleHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
                            mAllowWhileIdleCompatHistory.removeForPackage(pkg,
                                    UserHandle.getUserId(uid));
                            removeLocked(uid, REMOVE_REASON_UNDEFINED);
                        } else {
                            // external-applications-unavailable case
@@ -4965,7 +4981,10 @@ public class AlarmManagerService extends SystemService {
                if (isAllowedWhileIdleRestricted(alarm)) {
                    // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm while the
                    // device was in doze or battery saver.
                    mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
                    final AppWakeupHistory history = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0)
                            ? mAllowWhileIdleHistory
                            : mAllowWhileIdleCompatHistory;
                    history.recordAlarmForPackage(alarm.sourcePackage,
                            UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
                    mAlarmStore.updateAlarmDeliveries(a -> {
                        if (a.creatorUid != alarm.creatorUid || !isAllowedWhileIdleRestricted(a)) {
+148 −9
Original line number Diff line number Diff line
@@ -514,8 +514,16 @@ public class AlarmManagerServiceTest {
    }

    private void setAllowWhileIdleAlarm(int type, long triggerTime, PendingIntent pi,
            boolean unrestricted) {
        final int flags = unrestricted ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : FLAG_ALLOW_WHILE_IDLE;
            boolean unrestricted, boolean compat) {
        assertFalse("Alarm cannot be compat and unrestricted", unrestricted && compat);
        final int flags;
        if (unrestricted) {
            flags = FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
        } else if (compat) {
            flags = FLAG_ALLOW_WHILE_IDLE_COMPAT;
        } else {
            flags = FLAG_ALLOW_WHILE_IDLE;
        }
        setTestAlarm(type, triggerTime, pi, 0, flags, TEST_CALLING_UID);
    }

@@ -1600,13 +1608,13 @@ public class AlarmManagerServiceTest {
        final long firstTrigger = mNowElapsedTest + 10;
        for (int i = 0; i < quota; i++) {
            setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
                    getNewMockPendingIntent(), false);
                    getNewMockPendingIntent(), false, false);
            mNowElapsedTest = mTestTimer.getElapsed();
            mTestTimer.expire();
        }
        // This one should get deferred on set.
        setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
                getNewMockPendingIntent(), false);
                getNewMockPendingIntent(), false, false);
        final long expectedNextTrigger = firstTrigger + mAllowWhileIdleWindow;
        assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
                mTestTimer.getElapsed());
@@ -1618,6 +1626,108 @@ public class AlarmManagerServiceTest {
        assertEquals(expectedNextTrigger - 50, mTestTimer.getElapsed());
    }

    @Test
    public void allowWhileIdleCompatAlarmsWhileDeviceIdle() throws Exception {
        setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0);

        final long window = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + window + 1000,
                getNewMockPendingIntent());
        assertNotNull(mService.mPendingIdleUntil);

        final int quota = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
        final long firstTrigger = mNowElapsedTest + 10;
        for (int i = 0; i < quota; i++) {
            setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
                    getNewMockPendingIntent(), false, true);
            mNowElapsedTest = mTestTimer.getElapsed();
            mTestTimer.expire();
        }
        // This one should get deferred on set.
        setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
                getNewMockPendingIntent(), false, true);
        final long expectedNextTrigger = firstTrigger + window;
        assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
                mTestTimer.getElapsed());

        // Bring the idle until alarm back.
        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger - 50,
                getNewMockPendingIntent());
        assertEquals(expectedNextTrigger - 50, mService.mPendingIdleUntil.getWhenElapsed());
        assertEquals(expectedNextTrigger - 50, mTestTimer.getElapsed());
    }

    @Test
    public void allowWhileIdleCompatHistorySeparate() throws Exception {
        when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
                TEST_CALLING_PACKAGE)).thenReturn(true);
        when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);

        final int fullQuota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
        final int compatQuota = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;

        final long fullWindow = mAllowWhileIdleWindow;
        final long compatWindow = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;

        final long firstFullTrigger = mNowElapsedTest + 10;
        for (int i = 0; i < fullQuota; i++) {
            setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstFullTrigger + i,
                    getNewMockPendingIntent(), false, false);
            mNowElapsedTest = mTestTimer.getElapsed();
            mTestTimer.expire();
        }
        // This one should get deferred on set, as full quota is not available.
        setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstFullTrigger + fullQuota,
                getNewMockPendingIntent(), false, false);
        final long expectedNextFullTrigger = firstFullTrigger + fullWindow;
        assertEquals("Incorrect trigger when no quota left", expectedNextFullTrigger,
                mTestTimer.getElapsed());
        mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);

        // The following should be allowed, as compat quota should be free.
        for (int i = 0; i < compatQuota; i++) {
            final long trigger = mNowElapsedTest + 1;
            setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger, getNewMockPendingIntent(),
                    false, true);
            assertEquals(trigger, mTestTimer.getElapsed());
            mNowElapsedTest = mTestTimer.getElapsed();
            mTestTimer.expire();
        }

        // Refresh the state
        mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
        mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
        mService.mAllowWhileIdleCompatHistory.removeForPackage(TEST_CALLING_PACKAGE,
                TEST_CALLING_USER);

        // Now test with flipped order

        final long firstCompatTrigger = mNowElapsedTest + 10;
        for (int i = 0; i < compatQuota; i++) {
            setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstCompatTrigger + i,
                    getNewMockPendingIntent(), false, true);
            mNowElapsedTest = mTestTimer.getElapsed();
            mTestTimer.expire();
        }
        // This one should get deferred on set, as full quota is not available.
        setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstCompatTrigger + compatQuota,
                getNewMockPendingIntent(), false, true);
        final long expectedNextCompatTrigger = firstCompatTrigger + compatWindow;
        assertEquals("Incorrect trigger when no quota left", expectedNextCompatTrigger,
                mTestTimer.getElapsed());
        mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);

        // The following should be allowed, as full quota should be free.
        for (int i = 0; i < fullQuota; i++) {
            final long trigger = mNowElapsedTest + 1;
            setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger, getNewMockPendingIntent(),
                    false, false);
            assertEquals(trigger, mTestTimer.getElapsed());
            mNowElapsedTest = mTestTimer.getElapsed();
            mTestTimer.expire();
        }
    }

    @Test
    public void allowWhileIdleUnrestricted() throws Exception {
        setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0);
@@ -1634,7 +1744,7 @@ public class AlarmManagerServiceTest {
        final long firstTrigger = mNowElapsedTest + 10;
        for (int i = 0; i < numAlarms; i++) {
            setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
                    getNewMockPendingIntent(), true);
                    getNewMockPendingIntent(), true, false);
        }
        // All of them should fire as expected.
        for (int i = 0; i < numAlarms; i++) {
@@ -1736,7 +1846,7 @@ public class AlarmManagerServiceTest {
        final int quota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;

        testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
                getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
                getNewMockPendingIntent(), false, false), quota, mAllowWhileIdleWindow);

        // Refresh the state
        mService.removeLocked(TEST_CALLING_UID,
@@ -1744,7 +1854,7 @@ public class AlarmManagerServiceTest {
        mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);

        testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
                trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
                trigger, getNewMockPendingIntent(), false, false), quota, mAllowWhileIdleWindow);

        // Refresh the state
        mService.removeLocked(TEST_CALLING_UID,
@@ -1752,7 +1862,36 @@ public class AlarmManagerServiceTest {
        mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);

        testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
                getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
                getNewMockPendingIntent(), false, false), quota, mAllowWhileIdleWindow);
    }

    @Test
    public void allowWhileIdleCompatAlarmsInBatterySaver() throws Exception {
        when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
                TEST_CALLING_PACKAGE)).thenReturn(true);
        when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);

        final int quota = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
        final long window = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;

        testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
                getNewMockPendingIntent(), false, true), quota, window);

        // Refresh the state
        mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
        mService.mAllowWhileIdleCompatHistory.removeForPackage(TEST_CALLING_PACKAGE,
                TEST_CALLING_USER);

        testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
                trigger, getNewMockPendingIntent(), false, true), quota, window);

        // Refresh the state
        mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
        mService.mAllowWhileIdleCompatHistory.removeForPackage(TEST_CALLING_PACKAGE,
                TEST_CALLING_USER);

        testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
                getNewMockPendingIntent(), false, true), quota, window);
    }

    @Test