Make bad notifications crash their application.

Implement notification manager handling of bad notifications, to
call a new activity manager to have the owner's process crashed
(if there is one).

Change-Id: Ib15e8d0c598756f3b39c99cc2045c18e054daf6b
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 6f44e8e..3e2c122 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -164,16 +164,21 @@
         final String pkg;
         final String tag;
         final int id;
+        final int uid;
+        final int initialPid;
         ITransientNotification callback;
         int duration;
         final Notification notification;
         IBinder statusBarKey;
 
-        NotificationRecord(String pkg, String tag, int id, Notification notification)
+        NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
+                Notification notification)
         {
             this.pkg = pkg;
             this.tag = tag;
             this.id = id;
+            this.uid = uid;
+            this.initialPid = initialPid;
             this.notification = notification;
         }
 
@@ -304,10 +309,18 @@
             }
         }
 
-        public void onNotificationError(String pkg, String tag, int id, String message) {
+        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);
             cancelNotification(pkg, tag, id, 0, 0);
-            // TODO: Tell the activity manager.
+            long ident = Binder.clearCallingIdentity();
+            try {
+                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
+                        "Bad notification posted from package " + pkg
+                        + ": " + message);
+            } catch (RemoteException e) {
+            }
+            Binder.restoreCallingIdentity(ident);
         }
     };
 
@@ -663,6 +676,9 @@
     public void enqueueNotificationWithTag(String pkg, String tag, int id,
             Notification notification, int[] idOut)
     {
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        
         checkIncomingCall(pkg);
 
         // Limit the number of notifications that any given package except the android
@@ -708,7 +724,8 @@
         }
 
         synchronized (mNotificationList) {
-            NotificationRecord r = new NotificationRecord(pkg, tag, id, notification);
+            NotificationRecord r = new NotificationRecord(pkg, tag, id,
+                    callingUid, callingPid, notification);
             NotificationRecord old = null;
 
             int index = indexOfNotificationLocked(pkg, tag, id);
@@ -732,7 +749,8 @@
             }
 
             if (notification.icon != 0) {
-                StatusBarNotification n = new StatusBarNotification(pkg, id, tag, notification);
+                StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
+                        r.uid, r.initialPid, notification);
                 if (old != null && old.statusBarKey != null) {
                     r.statusBarKey = old.statusBarKey;
                     long identity = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 1a16387..4177432 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -85,7 +85,8 @@
         void onClearAll();
         void onNotificationClick(String pkg, String tag, int id);
         void onPanelRevealed();
-        void onNotificationError(String pkg, String tag, int id, String message);
+        void onNotificationError(String pkg, String tag, int id,
+                int uid, int initialPid, String message);
     }
 
     /**
@@ -293,11 +294,12 @@
         mNotificationCallbacks.onNotificationClick(pkg, tag, id);
     }
 
-    public void onNotificationError(String pkg, String tag, int id, String message) {
+    public void onNotificationError(String pkg, String tag, int id,
+            int uid, int initialPid, String message) {
         enforceStatusBarService();
 
         // WARNING: this will call back into us to do the remove.  Don't hold any locks.
-        mNotificationCallbacks.onNotificationError(pkg, tag, id, message);
+        mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message);
     }
 
     public void onClearAllNotifications() {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 0e1eb6f..252392b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4562,6 +4562,60 @@
         }
     }
     
+    public void crashApplication(int uid, int initialPid, String packageName,
+            String message) {
+        if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
+                != PackageManager.PERMISSION_GRANTED) {
+            String msg = "Permission Denial: crashApplication() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        
+        synchronized(this) {
+            ProcessRecord proc = null;
+            
+            // Figure out which process to kill.  We don't trust that initialPid
+            // still has any relation to current pids, so must scan through the
+            // list.
+            synchronized (mPidsSelfLocked) {
+                for (int i=0; i<mPidsSelfLocked.size(); i++) {
+                    ProcessRecord p = mPidsSelfLocked.valueAt(i);
+                    if (p.info.uid != uid) {
+                        continue;
+                    }
+                    if (p.pid == initialPid) {
+                        proc = p;
+                        break;
+                    }
+                    for (String str : p.pkgList) {
+                        if (str.equals(packageName)) {
+                            proc = p;
+                        }
+                    }
+                }
+            }
+            
+            if (proc == null) {
+                Log.w(TAG, "crashApplication: nothing for uid=" + uid
+                        + " initialPid=" + initialPid
+                        + " packageName=" + packageName);
+                return;
+            }
+            
+            if (proc.thread != null) {
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    proc.thread.scheduleCrash(message);
+                } catch (RemoteException e) {
+                }
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+    
     void sendActivityResultLocked(int callingUid, ActivityRecord r,
             String resultWho, int requestCode, int resultCode, Intent data) {