Fix crash when startForeground posts a broken Notification.

The NotificationManager tries to crash the calling app, but
in the case of a service calling startForeground, the caller
is the ActivityManager, so system_server goes down.

NotificationManagerService#enqueueNotificationInternal is a
new internal-only method that accepts a UID/PID to use when
punishing bogus notifications (such as the one in
http://b/2869787).

Change-Id: I84a9854bae630bc90288cebb94f174809d5dac8c
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 6fe12fc..1fae516 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.content.Context;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.os.Handler;
 import android.os.IBinder;
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 25de8b0..63325dd 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -67,7 +67,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 
-class NotificationManagerService extends INotificationManager.Stub
+/** {@hide} */
+public class NotificationManagerService extends INotificationManager.Stub
 {
     private static final String TAG = "NotificationService";
     private static final boolean DBG = false;
@@ -311,7 +312,8 @@
 
         public void onNotificationError(String pkg, String tag, int id,
                 int uid, int initialPid, String message) {
-            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id);
+            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
+                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
             cancelNotification(pkg, tag, id, 0, 0);
             long ident = Binder.clearCallingIdentity();
             try {
@@ -666,11 +668,20 @@
         enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
     }
 
-    public void enqueueNotificationWithTag(String pkg, String tag, int id,
-            Notification notification, int[] idOut)
+    public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
+            int[] idOut)
     {
-        final int callingUid = Binder.getCallingUid();
-        final int callingPid = Binder.getCallingPid();
+        enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
+                tag, id, notification, idOut);
+    }
+
+    // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
+    // uid/pid of another application)
+    public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
+            String tag, int id, Notification notification, int[] idOut)
+    {
+        Slog.d(TAG, "enqueueNotificationWithTag: calling uid=" + callingUid 
+                + ", pid=" + callingPid);
         
         checkIncomingCall(pkg);
 
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 0542497..75365ad 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.NotificationManagerService;
 
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -252,6 +253,8 @@
     }
     
     public void postNotification() {
+        final int appUid = appInfo.uid;
+        final int appPid = app.pid;
         if (foregroundId != 0 && foregroundNoti != null) {
             // Do asynchronous communication with notification manager to
             // avoid deadlocks.
@@ -260,14 +263,15 @@
             final Notification localForegroundNoti = foregroundNoti;
             ams.mHandler.post(new Runnable() {
                 public void run() {
-                    INotificationManager inm = NotificationManager.getService();
-                    if (inm == null) {
+                    NotificationManagerService nm =
+                            (NotificationManagerService) NotificationManager.getService();
+                    if (nm == null) {
                         return;
                     }
                     try {
                         int[] outId = new int[1];
-                        inm.enqueueNotification(localPackageName, localForegroundId,
-                                localForegroundNoti, outId);
+                        nm.enqueueNotificationInternal(localPackageName, appUid, appPid,
+                                null, localForegroundId, localForegroundNoti, outId);
                     } catch (RuntimeException e) {
                         Slog.w(ActivityManagerService.TAG,
                                 "Error showing notification for service", e);
@@ -275,7 +279,6 @@
                         // get to be foreground.
                         ams.setServiceForeground(name, ServiceRecord.this,
                                 localForegroundId, null, true);
-                    } catch (RemoteException e) {
                     }
                 }
             });