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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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--) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user