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

Commit 951fa94b authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

RESTRICT AUTOMERGE Remove alarms from mPendingNonWakeupAlarms

While setting an alarm, we should remove any existing occurrences
of the same, but we were skipping on removing any past due non-wakeup
alarms.
In rare cases when a caller is spamming the alarm manager, we
might end up accumulating alarms inside mPendingNonWakeupAlarms that
count towards the per-uid limit of the caller, and may result in an
exception.

Test: atest FrameworksMockingServicesTests:AlarmManagerServiceTest
atest CtsAlarmManagerTestCases

Bug: 167645096
Bug: 130444055
Change-Id: I2d2b08019a3b3a1e63b60d3a6e2909db7b1a1864
parent be69e09c
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -3109,6 +3109,14 @@ class AlarmManagerService extends SystemService {
                mPendingBackgroundAlarms.removeAt(i);
            }
        }
        for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) {
            final Alarm a = mPendingNonWakeupAlarms.get(i);
            if (a.matches(operation, directReceiver)) {
                // Don't set didRemove, since this doesn't impact the scheduled alarms.
                mPendingNonWakeupAlarms.remove(i);
                decrementAlarmCount(a.uid, 1);
            }
        }
        if (didRemove) {
            if (DEBUG_BATCH) {
                Slog.v(TAG, "remove(operation) changed bounds; rebatching");
+43 −0
Original line number Diff line number Diff line
@@ -1053,6 +1053,49 @@ public class AlarmManagerServiceTest {
        }
    }

    @Test
    public void nonWakeupAlarmsDeferred() throws Exception {
        final int numAlarms = 10;
        final PendingIntent[] pis = new PendingIntent[numAlarms];
        for (int i = 0; i < numAlarms; i++) {
            pis[i] = getNewMockPendingIntent();
            setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]);
        }
        doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong());
        // Advance time past all expirations.
        mNowElapsedTest += numAlarms + 5;
        mTestTimer.expire();
        assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());

        // These alarms should be sent on interactive state change to true
        mService.interactiveStateChangedLocked(false);
        mService.interactiveStateChangedLocked(true);

        for (int i = 0; i < numAlarms; i++) {
            verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(),
                    any(Handler.class), isNull(), any());
        }
    }

    @Test
    public void alarmCountOnPendingNonWakeupAlarmsRemoved() throws Exception {
        final int numAlarms = 10;
        final PendingIntent[] pis = new PendingIntent[numAlarms];
        for (int i = 0; i < numAlarms; i++) {
            pis[i] = getNewMockPendingIntent();
            setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]);
        }
        doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong());
        // Advance time past all expirations.
        mNowElapsedTest += numAlarms + 5;
        mTestTimer.expire();
        assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());
        for (int i = 0; i < numAlarms; i++) {
            mService.removeLocked(pis[i], null);
            assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
        }
    }

    @After
    public void tearDown() {
        if (mMockingSession != null) {