Cancel alarms & jobs when an app's data is cleared
In the same bit of code, fix a system restore issue: in the
course of setup + restore, we reestablish permission grants and
notification state up front for the to-be-restored app, and
then bring in its data. However, a data wipe is part of the
prologue for that data delivery -- so we were inadvertently
unwinding the permission grants and notification state restore
that we'd just performed. Now, we distinguish the restore flow
from other clear-data operations so we don't unwind that operation.
Finally take the opportunity to elide a lot of copypasta code into
a single predicate-driven implementation.
Bug: 67508896
Bug: 69538432
Test: atest android.app.cts.AlarmManagerTest
Change-Id: I15c912c3c99645599ae9bd6fb7337fa86b4e5757
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index ca15249..ae4f0ce 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -92,6 +92,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.LocalLog;
import com.android.server.ForceAppStandbyTracker.Listener;
+import com.android.server.LocalServices;
/**
* Alarm manager implementaion.
@@ -467,21 +468,14 @@
return newStart;
}
- boolean remove(final PendingIntent operation, final IAlarmListener listener) {
- if (operation == null && listener == null) {
- if (localLOGV) {
- Slog.w(TAG, "requested remove() of null operation",
- new RuntimeException("here"));
- }
- return false;
- }
+ boolean remove(Predicate<Alarm> predicate) {
boolean didRemove = false;
long newStart = 0; // recalculate endpoints as we go
long newEnd = Long.MAX_VALUE;
int newFlags = 0;
for (int i = 0; i < alarms.size(); ) {
Alarm alarm = alarms.get(i);
- if (alarm.matches(operation, listener)) {
+ if (predicate.test(alarm)) {
alarms.remove(i);
didRemove = true;
if (alarm.alarmClock != null) {
@@ -507,111 +501,6 @@
return didRemove;
}
- boolean remove(final String packageName) {
- if (packageName == null) {
- if (localLOGV) {
- Slog.w(TAG, "requested remove() of null packageName",
- new RuntimeException("here"));
- }
- return false;
- }
- boolean didRemove = false;
- long newStart = 0; // recalculate endpoints as we go
- long newEnd = Long.MAX_VALUE;
- int newFlags = 0;
- for (int i = alarms.size()-1; i >= 0; i--) {
- Alarm alarm = alarms.get(i);
- if (alarm.matches(packageName)) {
- alarms.remove(i);
- didRemove = true;
- if (alarm.alarmClock != null) {
- mNextAlarmClockMayChange = true;
- }
- } else {
- if (alarm.whenElapsed > newStart) {
- newStart = alarm.whenElapsed;
- }
- if (alarm.maxWhenElapsed < newEnd) {
- newEnd = alarm.maxWhenElapsed;
- }
- newFlags |= alarm.flags;
- }
- }
- if (didRemove) {
- // commit the new batch bounds
- start = newStart;
- end = newEnd;
- flags = newFlags;
- }
- return didRemove;
- }
-
- boolean removeForStopped(final int uid) {
- boolean didRemove = false;
- long newStart = 0; // recalculate endpoints as we go
- long newEnd = Long.MAX_VALUE;
- int newFlags = 0;
- for (int i = alarms.size()-1; i >= 0; i--) {
- Alarm alarm = alarms.get(i);
- try {
- if (alarm.uid == uid && ActivityManager.getService().isAppStartModeDisabled(
- uid, alarm.packageName)) {
- alarms.remove(i);
- didRemove = true;
- if (alarm.alarmClock != null) {
- mNextAlarmClockMayChange = true;
- }
- } else {
- if (alarm.whenElapsed > newStart) {
- newStart = alarm.whenElapsed;
- }
- if (alarm.maxWhenElapsed < newEnd) {
- newEnd = alarm.maxWhenElapsed;
- }
- newFlags |= alarm.flags;
- }
- } catch (RemoteException e) {
- }
- }
- if (didRemove) {
- // commit the new batch bounds
- start = newStart;
- end = newEnd;
- flags = newFlags;
- }
- return didRemove;
- }
-
- boolean remove(final int userHandle) {
- boolean didRemove = false;
- long newStart = 0; // recalculate endpoints as we go
- long newEnd = Long.MAX_VALUE;
- for (int i = 0; i < alarms.size(); ) {
- Alarm alarm = alarms.get(i);
- if (UserHandle.getUserId(alarm.creatorUid) == userHandle) {
- alarms.remove(i);
- didRemove = true;
- if (alarm.alarmClock != null) {
- mNextAlarmClockMayChange = true;
- }
- } else {
- if (alarm.whenElapsed > newStart) {
- newStart = alarm.whenElapsed;
- }
- if (alarm.maxWhenElapsed < newEnd) {
- newEnd = alarm.maxWhenElapsed;
- }
- i++;
- }
- }
- if (didRemove) {
- // commit the new batch bounds
- start = newStart;
- end = newEnd;
- }
- return didRemove;
- }
-
boolean hasPackage(final String packageName) {
final int N = alarms.size();
for (int i = 0; i < N; i++) {
@@ -759,6 +648,8 @@
mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
+
+ publishLocalService(AlarmManagerInternal.class, new LocalService());
}
static long convertToElapsed(long when, int type) {
@@ -1554,6 +1445,21 @@
}
}
+ /**
+ * System-process internal API
+ */
+ private final class LocalService implements AlarmManagerInternal {
+ @Override
+ public void removeAlarmsForUid(int uid) {
+ synchronized (mLock) {
+ removeLocked(uid);
+ }
+ }
+ }
+
+ /**
+ * Public-facing binder interface
+ */
private final IBinder mService = new IAlarmManager.Stub() {
@Override
public void set(String callingPackage,
@@ -2430,10 +2336,19 @@
}
private void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
+ if (operation == null && directReceiver == null) {
+ if (localLOGV) {
+ Slog.w(TAG, "requested remove() of null operation",
+ new RuntimeException("here"));
+ }
+ return;
+ }
+
boolean didRemove = false;
+ final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(operation, directReceiver);
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(operation, directReceiver);
+ didRemove |= b.remove(whichAlarms);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -2476,11 +2391,58 @@
}
}
- void removeLocked(String packageName) {
+ void removeLocked(final int uid) {
boolean didRemove = false;
+ final Predicate<Alarm> whichAlarms = (Alarm a) -> a.uid == uid;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(packageName);
+ didRemove |= b.remove(whichAlarms);
+ if (b.size() == 0) {
+ mAlarmBatches.remove(i);
+ }
+ }
+ for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
+ final Alarm a = mPendingWhileIdleAlarms.get(i);
+ if (a.uid == uid) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ mPendingWhileIdleAlarms.remove(i);
+ }
+ }
+ for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i --) {
+ final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
+ for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
+ if (alarmsForUid.get(j).uid == uid) {
+ alarmsForUid.remove(j);
+ }
+ }
+ if (alarmsForUid.size() == 0) {
+ mPendingBackgroundAlarms.removeAt(i);
+ }
+ }
+ if (didRemove) {
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, "remove(uid) changed bounds; rebatching");
+ }
+ rebatchAllAlarmsLocked(true);
+ rescheduleKernelAlarmsLocked();
+ updateNextAlarmClockLocked();
+ }
+ }
+
+ void removeLocked(final String packageName) {
+ if (packageName == null) {
+ if (localLOGV) {
+ Slog.w(TAG, "requested remove() of null packageName",
+ new RuntimeException("here"));
+ }
+ return;
+ }
+
+ boolean didRemove = false;
+ final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(packageName);
+ for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+ Batch b = mAlarmBatches.get(i);
+ didRemove |= b.remove(whichAlarms);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -2513,11 +2475,20 @@
}
}
- void removeForStoppedLocked(int uid) {
+ void removeForStoppedLocked(final int uid) {
boolean didRemove = false;
+ final Predicate<Alarm> whichAlarms = (Alarm a) -> {
+ try {
+ if (a.uid == uid && ActivityManager.getService().isAppStartModeDisabled(
+ uid, a.packageName)) {
+ return true;
+ }
+ } catch (RemoteException e) { /* fall through */}
+ return false;
+ };
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.removeForStopped(uid);
+ didRemove |= b.remove(whichAlarms);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -2546,9 +2517,11 @@
void removeUserLocked(int userHandle) {
boolean didRemove = false;
+ final Predicate<Alarm> whichAlarms =
+ (Alarm a) -> UserHandle.getUserId(a.creatorUid) == userHandle;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(userHandle);
+ didRemove |= b.remove(whichAlarms);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -3396,6 +3369,7 @@
@Override
public void onReceive(Context context, Intent intent) {
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
synchronized (mLock) {
String action = intent.getAction();
String pkgList[] = null;
@@ -3416,7 +3390,6 @@
removeUserLocked(userHandle);
}
} else if (Intent.ACTION_UID_REMOVED.equals(action)) {
- int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (uid >= 0) {
mLastAllowWhileIdleDispatch.delete(uid);
}
@@ -3436,7 +3409,13 @@
}
if (pkgList != null && (pkgList.length > 0)) {
for (String pkg : pkgList) {
- removeLocked(pkg);
+ if (uid >= 0) {
+ // package-removed case
+ removeLocked(uid);
+ } else {
+ // external-applications-unavailable etc case
+ removeLocked(pkg);
+ }
mPriorities.remove(pkg);
for (int i=mBroadcastStats.size()-1; i>=0; i--) {
ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);