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
This commit is contained in:
Christopher Tate
2021-06-24 15:46:58 -07:00
parent c46c9e644f
commit 0b3f85a600
2 changed files with 38 additions and 7 deletions

View File

@@ -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();

View File

@@ -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;