Fix issue #29506774: Foreground Service Can Avoid Notification Requirement

Don't cancel the notification if there are other foreground
services using the same notification ID.

Change-Id: I02a49d9a07af0203e59e70be2dc6773f3cefee47
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ee2fa51..a3d2ebb 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -699,7 +699,7 @@
                         throw new IllegalArgumentException("null notification");
                     }
                     if (r.foregroundId != id) {
-                        r.cancelNotification();
+                        cancelForegroudNotificationLocked(r);
                         r.foregroundId = id;
                     }
                     notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
@@ -721,7 +721,7 @@
                         }
                     }
                     if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
-                        r.cancelNotification();
+                        cancelForegroudNotificationLocked(r);
                         r.foregroundId = 0;
                         r.foregroundNoti = null;
                     } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
@@ -738,6 +738,27 @@
         }
     }
 
+    private void cancelForegroudNotificationLocked(ServiceRecord r) {
+        if (r.foregroundId != 0) {
+            // First check to see if this app has any other active foreground services
+            // with the same notification ID.  If so, we shouldn't actually cancel it,
+            // because that would wipe away the notification that still needs to be shown
+            // due the other service.
+            ServiceMap sm = getServiceMap(r.userId);
+            if (sm != null) {
+                for (int i = sm.mServicesByName.size()-1; i >= 0; i--) {
+                    ServiceRecord other = sm.mServicesByName.valueAt(i);
+                    if (other.foregroundId == r.foregroundId
+                            && other.packageName.equals(r.packageName)) {
+                        // Found one!  Abort the cancel.
+                        return;
+                    }
+                }
+            }
+            r.cancelNotification();
+        }
+    }
+
     private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
         boolean anyForeground = false;
         for (int i=proc.services.size()-1; i>=0; i--) {
@@ -1560,7 +1581,7 @@
             r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
         }
 
-        r.cancelNotification();
+        cancelForegroudNotificationLocked(r);
 
         mAm.mHandler.removeCallbacks(r.restarter);
         mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
@@ -2022,7 +2043,7 @@
             }
         }
 
-        r.cancelNotification();
+        cancelForegroudNotificationLocked(r);
         r.isForeground = false;
         r.foregroundId = 0;
         r.foregroundNoti = null;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 2bfc402..71c7fd3 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -543,27 +543,25 @@
     }
     
     public void cancelNotification() {
-        if (foregroundId != 0) {
-            // Do asynchronous communication with notification manager to
-            // avoid deadlocks.
-            final String localPackageName = packageName;
-            final int localForegroundId = foregroundId;
-            ams.mHandler.post(new Runnable() {
-                public void run() {
-                    INotificationManager inm = NotificationManager.getService();
-                    if (inm == null) {
-                        return;
-                    }
-                    try {
-                        inm.cancelNotificationWithTag(localPackageName, null,
-                                localForegroundId, userId);
-                    } catch (RuntimeException e) {
-                        Slog.w(TAG, "Error canceling notification for service", e);
-                    } catch (RemoteException e) {
-                    }
+        // Do asynchronous communication with notification manager to
+        // avoid deadlocks.
+        final String localPackageName = packageName;
+        final int localForegroundId = foregroundId;
+        ams.mHandler.post(new Runnable() {
+            public void run() {
+                INotificationManager inm = NotificationManager.getService();
+                if (inm == null) {
+                    return;
                 }
-            });
-        }
+                try {
+                    inm.cancelNotificationWithTag(localPackageName, null,
+                            localForegroundId, userId);
+                } catch (RuntimeException e) {
+                    Slog.w(TAG, "Error canceling notification for service", e);
+                } catch (RemoteException e) {
+                }
+            }
+        });
     }
 
     public void stripForegroundServiceFlagFromNotification() {