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

Commit ca41ed61 authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

Adding an API to cancel all alarms

In some cases, an app can lose access to the pending intents it used to
set previous alarms. In the current state, such alarms can never be
cancelled. This can be an issue because there is now a maximum limit to
the number of concurrent alarms an app can set.
An API to cancel all pending alarms set by the caller should help
mitigate this.
It should be noted that an API to query or retrieve all pending alarms
is not straightforward with the current design.

Test: atest CtsAlarmManagerTestCases:BasicApiTests
Test: atest FrameworksMockingServicesTests:AlarmManagerServiceTest

Bug: 182859584
Change-Id: If8f81f93838bf366887c63ae006f9291ee636f5c
parent 7d107143
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -1256,6 +1256,17 @@ public class AlarmManager {
        wrapper.cancel();
    }

    /**
     * Remove all alarms previously set by the caller, if any.
     */
    public void cancelAll() {
        try {
            mService.removeAll(mContext.getOpPackageName());
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

    /**
     * Set the system wall clock time.
     * Requires the permission android.permission.SET_TIME.
+1 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ interface IAlarmManager {
    boolean setTime(long millis);
    void setTimeZone(String zone);
    void remove(in PendingIntent operation, in IAlarmListener listener);
    void removeAll(String packageName);
    long getNextWakeFromIdleTime();
    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
    AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
+21 −3
Original line number Diff line number Diff line
@@ -2965,6 +2965,24 @@ public class AlarmManagerService extends SystemService {
            }
        }

        @Override
        public void removeAll(String callingPackage) {
            final int callingUid = mInjector.getCallingUid();
            if (callingUid == Process.SYSTEM_UID) {
                Slog.wtfStack(TAG, "Attempt to remove all alarms from the system uid package: "
                        + callingPackage);
                return;
            }
            if (callingUid != mPackageManagerInternal.getPackageUid(callingPackage, 0,
                    UserHandle.getUserId(callingUid))) {
                throw new SecurityException("Package " + callingPackage
                        + " does not belong to the calling uid " + callingUid);
            }
            synchronized (mLock) {
                removeLocked(callingPackage, REMOVE_REASON_ALARM_CANCELLED);
            }
        }

        @Override
        public long getNextWakeFromIdleTime() {
            return getNextWakeFromIdleTimeImpl();
@@ -4077,7 +4095,7 @@ public class AlarmManagerService extends SystemService {
    }

    @GuardedBy("mLock")
    void removeLocked(final String packageName) {
    void removeLocked(final String packageName, int reason) {
        if (packageName == null) {
            if (localLOGV) {
                Slog.w(TAG, "requested remove() of null packageName",
@@ -4085,7 +4103,7 @@ public class AlarmManagerService extends SystemService {
            }
            return;
        }
        removeAlarmsInternalLocked(a -> a.matches(packageName), REMOVE_REASON_UNDEFINED);
        removeAlarmsInternalLocked(a -> a.matches(packageName), reason);
    }

    // Only called for ephemeral apps
@@ -5128,7 +5146,7 @@ public class AlarmManagerService extends SystemService {
                            removeLocked(uid, REMOVE_REASON_UNDEFINED);
                        } else {
                            // external-applications-unavailable case
                            removeLocked(pkg);
                            removeLocked(pkg, REMOVE_REASON_UNDEFINED);
                        }
                        mPriorities.remove(pkg);
                        for (int i = mBroadcastStats.size() - 1; i >= 0; i--) {
+1 −0
Original line number Diff line number Diff line
@@ -4596,6 +4596,7 @@ package android.app {
    method public boolean canScheduleExactAlarms();
    method public void cancel(android.app.PendingIntent);
    method public void cancel(android.app.AlarmManager.OnAlarmListener);
    method public void cancelAll();
    method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock();
    method public void set(int, long, android.app.PendingIntent);
    method public void set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
+58 −4
Original line number Diff line number Diff line
@@ -254,6 +254,7 @@ public class AlarmManagerServiceTest {
    private Injector mInjector;
    private volatile long mNowElapsedTest;
    private volatile long mNowRtcTest;
    private volatile int mTestCallingUid = TEST_CALLING_UID;
    @GuardedBy("mTestTimer")
    private TestTimer mTestTimer = new TestTimer();

@@ -328,7 +329,7 @@ public class AlarmManagerServiceTest {

        @Override
        int getCallingUid() {
            return TEST_CALLING_UID;
            return mTestCallingUid;
        }

        @Override
@@ -1395,7 +1396,7 @@ public class AlarmManagerServiceTest {
            setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10, getNewMockPendingIntent());
        }
        assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
        mService.removeLocked(TEST_CALLING_PACKAGE);
        mService.removeLocked(TEST_CALLING_PACKAGE, REMOVE_REASON_UNDEFINED);
        assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
    }

@@ -2677,6 +2678,59 @@ public class AlarmManagerServiceTest {
                eq(EXACT_ALLOW_REASON_ALLOW_LIST));
    }

    @Test
    public void removeAllBinderCall() throws RemoteException {
        for (int i = 0; i < 10; i++) {
            setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 1, getNewMockPendingIntent());
        }

        final String otherUidPackage1 = "other.uid.package1";
        final String otherUidPackage2 = "other.uid.package2";
        final int otherUid = 1243;

        registerAppIds(
                new String[]{TEST_CALLING_PACKAGE, otherUidPackage1, otherUidPackage2},
                new Integer[]{TEST_CALLING_UID, otherUid, otherUid}
        );

        for (int i = 0; i < 9; i++) {
            setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 11, 0,
                    getNewMockPendingIntent(otherUid, otherUidPackage1), 0, 0, otherUid,
                    otherUidPackage1, null);
        }

        for (int i = 0; i < 8; i++) {
            setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 20, 0,
                    getNewMockPendingIntent(otherUid, otherUidPackage2), 0, 0, otherUid,
                    otherUidPackage2, null);
        }

        assertEquals(27, mService.mAlarmStore.size());

        try {
            mBinder.removeAll(otherUidPackage1);
            fail("removeAll() for wrong package did not throw SecurityException");
        } catch (SecurityException se) {
            // Expected
        }

        try {
            mBinder.removeAll(otherUidPackage2);
            fail("removeAll() for wrong package did not throw SecurityException");
        } catch (SecurityException se) {
            // Expected
        }

        mBinder.removeAll(TEST_CALLING_PACKAGE);
        assertEquals(17, mService.mAlarmStore.size());
        assertEquals(0, mService.mAlarmStore.getCount(a -> a.matches(TEST_CALLING_PACKAGE)));

        mTestCallingUid = otherUid;
        mBinder.removeAll(otherUidPackage1);
        assertEquals(0, mService.mAlarmStore.getCount(a -> a.matches(otherUidPackage1)));
        assertEquals(8, mService.mAlarmStore.getCount(a -> a.matches(otherUidPackage2)));
    }

    @Test
    public void minWindowChangeEnabled() {
        mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true);
@@ -3572,7 +3626,7 @@ public class AlarmManagerServiceTest {
                getNewMockPendingIntent()), standbyQuota + temporaryQuota, mAppStandbyWindow);

        // refresh the state.
        mService.removeLocked(TEST_CALLING_PACKAGE);
        mService.removeLocked(TEST_CALLING_PACKAGE, REMOVE_REASON_UNDEFINED);
        mService.mAppWakeupHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
        mService.mTemporaryQuotaReserve.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);

@@ -3581,7 +3635,7 @@ public class AlarmManagerServiceTest {
                getNewMockPendingIntent()), standbyQuota + temporaryQuota, mAppStandbyWindow);

        // refresh the state.
        mService.removeLocked(TEST_CALLING_PACKAGE);
        mService.removeLocked(TEST_CALLING_PACKAGE, REMOVE_REASON_UNDEFINED);
        mService.mAppWakeupHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
        mService.mTemporaryQuotaReserve.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);