From 0b3f85a600a41bf6920ce8ce7776d0fde2c13e29 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Thu, 24 Jun 2021 15:46:58 -0700 Subject: [PATCH] Properly reset FGS notification associations When the service is brought down, the notification needs to be properly freed from FGS-related policy. When the same notification is being shared by multiple live FGS instances, it needs to remain flagged until the last of them exits that state. Along the way, fix a latent bug that resulted in incorrect notification behavior when an FGS's associated notification had id 0. Bug: 191518901 Test: CtsAppTestCases:android.app.cts.ServiceTest Change-Id: If8bae1c04d98cbab848823c86951544f792efba4 --- .../com/android/server/am/ActiveServices.java | 34 +++++++++++++++++-- .../com/android/server/am/ServiceRecord.java | 11 +++--- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 3b3febea0272e..89781d3fbb0a5 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1976,7 +1976,7 @@ public final class ActiveServices { r.foregroundId = 0; r.foregroundNoti = null; } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) { - r.stripForegroundServiceFlagFromNotification(); + dropFgsNotificationStateLocked(r); if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) { r.foregroundId = 0; r.foregroundNoti = null; @@ -4225,9 +4225,10 @@ public final class ActiveServices { } r.isForeground = false; + r.mFgsNotificationWasDeferred = false; + dropFgsNotificationStateLocked(r); r.foregroundId = 0; r.foregroundNoti = null; - r.mFgsNotificationWasDeferred = false; resetFgsRestrictionLocked(r); // Clear start entries. @@ -4291,6 +4292,35 @@ public final class ActiveServices { smap.ensureNotStartingBackgroundLocked(r); } + private void dropFgsNotificationStateLocked(ServiceRecord r) { + // If this is the only FGS using this notification, clear its FGS flag + boolean shared = false; + final ServiceMap smap = mServiceMap.get(r.userId); + if (smap != null) { + // Is any other FGS using this notification? + final int numServices = smap.mServicesByInstanceName.size(); + for (int i = 0; i < numServices; i++) { + final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i); + if (sr == r) { + continue; + } + if (sr.isForeground + && r.foregroundId == sr.foregroundId + && r.appInfo.packageName.equals(sr.appInfo.packageName)) { + shared = true; + break; + } + } + } else { + Slog.wtf(TAG, "FGS " + r + " not found!"); + } + + // No other FGS is sharing this notification, so we're done with it + if (!shared) { + r.stripForegroundServiceFlagFromNotification(); + } + } + void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp, ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) { IBinder binder = c.conn.asBinder(); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 141f081ab9a7a..804e442bc8dea 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -20,6 +20,7 @@ import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static android.os.PowerExemptionManager.REASON_DENIED; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -928,13 +929,17 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN public void postNotification() { final int appUid = appInfo.uid; final int appPid = app.getPid(); - if (foregroundId != 0 && foregroundNoti != null) { + if (isForeground && foregroundNoti != null) { // Do asynchronous communication with notification manager to // avoid deadlocks. final String localPackageName = packageName; final int localForegroundId = foregroundId; final Notification _foregroundNoti = foregroundNoti; final ServiceRecord record = this; + if (DEBUG_FOREGROUND_SERVICE) { + Slog.d(TAG, "Posting notification " + _foregroundNoti + + " for foreground service " + this); + } ams.mHandler.post(new Runnable() { public void run() { NotificationManagerInternal nm = LocalServices.getService( @@ -1066,10 +1071,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } public void stripForegroundServiceFlagFromNotification() { - if (foregroundId == 0) { - return; - } - final int localForegroundId = foregroundId; final int localUserId = userId; final String localPackageName = packageName;