add a optional String to the key of notifications to allow users
to scope them
diff --git a/api/current.xml b/api/current.xml
index 3612104e..54cff8d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -23166,6 +23166,21 @@
 <parameter name="id" type="int">
 </parameter>
 </method>
+<method name="cancel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+<parameter name="id" type="int">
+</parameter>
+</method>
 <method name="cancelAll"
  return="void"
  abstract="false"
@@ -23192,6 +23207,23 @@
 <parameter name="notification" type="android.app.Notification">
 </parameter>
 </method>
+<method name="notify"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="notification" type="android.app.Notification">
+</parameter>
+</method>
 </class>
 <class name="PendingIntent"
  extends="java.lang.Object"
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index c1035b6..4d5238c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -24,11 +24,15 @@
 /** {@hide} */
 interface INotificationManager
 {
+    /** @deprecated use {@link #enqueueNotificationWithTag} instead */
     void enqueueNotification(String pkg, int id, in Notification notification, inout int[] idReceived);
+    /** @deprecated use {@link #cancelNotificationWithTag} instead */
     void cancelNotification(String pkg, int id);
     void cancelAllNotifications(String pkg);
 
     void enqueueToast(String pkg, ITransientNotification callback, int duration);
     void cancelToast(String pkg, ITransientNotification callback);
+    void enqueueNotificationWithTag(String pkg, String tag, int id, in Notification notification, inout int[] idReceived);
+    void cancelNotificationWithTag(String pkg, String tag, int id);
 }
 
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 7b51fdf..6fe12fc 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -87,12 +87,27 @@
      */
     public void notify(int id, Notification notification)
     {
+        notify(null, id, notification);
+    }
+
+    /**
+     * Persistent notification on the status bar,
+     *
+     * @param tag An string identifier for this notification unique within your
+     *        application.
+     * @param notification A {@link Notification} object describing how to
+     *        notify the user, other than the view you're providing. Must not be null.
+     * @return the id of the notification that is associated with the string identifier that
+     * can be used to cancel the notification
+     */
+    public void notify(String tag, int id, Notification notification)
+    {
         int[] idOut = new int[1];
         INotificationManager service = getService();
         String pkg = mContext.getPackageName();
         if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
         try {
-            service.enqueueNotification(pkg, id, notification, idOut);
+            service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut);
             if (id != idOut[0]) {
                 Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
             }
@@ -107,11 +122,21 @@
      */
     public void cancel(int id)
     {
+        cancel(null, id);
+    }
+
+    /**
+     * Cancel a previously shown notification.  If it's transient, the view
+     * will be hidden.  If it's persistent, it will be removed from the status
+     * bar.
+     */
+    public void cancel(String tag, int id)
+    {
         INotificationManager service = getService();
         String pkg = mContext.getPackageName();
         if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
         try {
-            service.cancelNotification(pkg, id);
+            service.cancelNotificationWithTag(pkg, tag, id);
         } catch (RemoteException e) {
         }
     }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 696ef31..ff23a13 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -30,7 +30,6 @@
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentQueryMap;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -103,7 +102,8 @@
     private boolean mAdbNotificationShown = false;
     private Notification mAdbNotification;
     
-    private ArrayList<NotificationRecord> mNotificationList;
+    private final ArrayList<NotificationRecord> mNotificationList =
+            new ArrayList<NotificationRecord>();
 
     private ArrayList<ToastRecord> mToastQueue;
 
@@ -152,20 +152,22 @@
 
     private static final class NotificationRecord
     {
-        String pkg;
-        int id;
+        final String pkg;
+        final String tag;
+        final int id;
         ITransientNotification callback;
         int duration;
-        Notification notification;
+        final Notification notification;
         IBinder statusBarKey;
 
-        NotificationRecord(String pkg, int id, Notification notification)
+        NotificationRecord(String pkg, String tag, int id, Notification notification)
         {
             this.pkg = pkg;
+            this.tag = tag;
             this.id = id;
             this.notification = notification;
         }
-        
+
         void dump(PrintWriter pw, String prefix, Context baseContext) {
             pw.println(prefix + this);
             pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
@@ -189,7 +191,8 @@
             return "NotificationRecord{"
                 + Integer.toHexString(System.identityHashCode(this))
                 + " pkg=" + pkg
-                + " id=" + Integer.toHexString(id) + "}";
+                + " id=" + Integer.toHexString(id)
+                + " tag=" + tag + "}";
         }
     }
 
@@ -258,8 +261,8 @@
             cancelAll();
         }
 
-        public void onNotificationClick(String pkg, int id) {
-            cancelNotification(pkg, id, Notification.FLAG_AUTO_CANCEL,
+        public void onNotificationClick(String pkg, String tag, int id) {
+            cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
                     Notification.FLAG_FOREGROUND_SERVICE);
         }
 
@@ -369,7 +372,6 @@
         mSound = new AsyncPlayer(TAG);
         mSound.setUsesWakeLock(context);
         mToastQueue = new ArrayList<ToastRecord>();
-        mNotificationList = new ArrayList<NotificationRecord>();
         mHandler = new WorkerHandler();
         mStatusBarService = statusBar;
         statusBar.setNotificationCallbacks(mNotificationCallbacks);
@@ -583,6 +585,12 @@
     // ============================================================================
     public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
     {
+        enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
+    }
+
+    public void enqueueNotificationWithTag(String pkg, String tag, int id,
+            Notification notification, int[] idOut)
+    {
         checkIncomingCall(pkg);
         
         // This conditional is a dirty hack to limit the logging done on
@@ -608,10 +616,10 @@
         }
 
         synchronized (mNotificationList) {
-            NotificationRecord r = new NotificationRecord(pkg, id, notification);
+            NotificationRecord r = new NotificationRecord(pkg, tag, id, notification);
             NotificationRecord old = null;
 
-            int index = indexOfNotificationLocked(pkg, id);
+            int index = indexOfNotificationLocked(pkg, tag, id);
             if (index < 0) {
                 mNotificationList.add(r);
             } else {
@@ -645,17 +653,18 @@
                 }
 
                 NotificationData n = new NotificationData();
-                    n.id = id;
-                    n.pkg = pkg;
-                    n.when = notification.when;
-                    n.tickerText = truncatedTicker;
-                    n.ongoingEvent = (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
-                    if (!n.ongoingEvent && (notification.flags & Notification.FLAG_NO_CLEAR) == 0) {
-                        n.clearable = true;
-                    }
-                    n.contentView = notification.contentView;
-                    n.contentIntent = notification.contentIntent;
-                    n.deleteIntent = notification.deleteIntent;
+                n.pkg = pkg;
+                n.tag = tag;
+                n.id = id;
+                n.when = notification.when;
+                n.tickerText = truncatedTicker;
+                n.ongoingEvent = (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
+                if (!n.ongoingEvent && (notification.flags & Notification.FLAG_NO_CLEAR) == 0) {
+                    n.clearable = true;
+                }
+                n.contentView = notification.contentView;
+                n.contentIntent = notification.contentIntent;
+                n.deleteIntent = notification.deleteIntent;
                 if (old != null && old.statusBarKey != null) {
                     r.statusBarKey = old.statusBarKey;
                     long identity = Binder.clearCallingIdentity();
@@ -828,16 +837,14 @@
      * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
      * and none of the {@code mustNotHaveFlags}. 
      */
-    private void cancelNotification(String pkg, int id, int mustHaveFlags,
+    private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
             int mustNotHaveFlags) {
         EventLog.writeEvent(EVENT_LOG_CANCEL, pkg, id, mustHaveFlags);
 
         synchronized (mNotificationList) {
-            NotificationRecord r = null;
-
-            int index = indexOfNotificationLocked(pkg, id);
+            int index = indexOfNotificationLocked(pkg, tag, id);
             if (index >= 0) {
-                r = mNotificationList.get(index);
+                NotificationRecord r = mNotificationList.get(index);
                 
                 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
                     return;
@@ -888,9 +895,13 @@
 
     
     public void cancelNotification(String pkg, int id) {
+        cancelNotificationWithTag(pkg, null /* tag */, id);
+    }
+
+    public void cancelNotificationWithTag(String pkg, String tag, int id) {
         checkIncomingCall(pkg);
         // Don't allow client applications to cancel foreground service notis.
-        cancelNotification(pkg, id, 0,
+        cancelNotification(pkg, tag, id, 0,
                 Binder.getCallingUid() == Process.SYSTEM_UID
                 ? 0 : Notification.FLAG_FOREGROUND_SERVICE);
     }
@@ -999,12 +1010,21 @@
     }
 
     // lock on mNotificationList
-    private int indexOfNotificationLocked(String pkg, int id)
+    private int indexOfNotificationLocked(String pkg, String tag, int id)
     {
         ArrayList<NotificationRecord> list = mNotificationList;
         final int len = list.size();
         for (int i=0; i<len; i++) {
             NotificationRecord r = list.get(i);
+            if (tag == null) {
+                if (r.tag != null) {
+                    continue;
+                }
+            } else {
+                if (!tag.equals(r.tag)) {
+                    continue;
+                }
+            }
             if (r.id == id && r.pkg.equals(pkg)) {
                 return i;
             }
diff --git a/services/java/com/android/server/status/NotificationData.java b/services/java/com/android/server/status/NotificationData.java
index 63a7d70..0a3411a1 100644
--- a/services/java/com/android/server/status/NotificationData.java
+++ b/services/java/com/android/server/status/NotificationData.java
@@ -5,6 +5,7 @@
 
 public class NotificationData {
     public String pkg;
+    public String tag;
     public int id;
     public CharSequence tickerText;
 
@@ -17,9 +18,6 @@
 
     public PendingIntent deleteIntent;
 
-    public NotificationData() {
-    }
-
     public String toString() {
         return "NotificationData(package=" + pkg + " tickerText=" + tickerText
                 + " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 83552dd..9929498 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -126,7 +126,7 @@
     public interface NotificationCallbacks {
         void onSetDisabled(int status);
         void onClearAll();
-        void onNotificationClick(String pkg, int id);
+        void onNotificationClick(String pkg, String tag, int id);
         void onPanelRevealed();
     }
 
@@ -833,7 +833,7 @@
         content.setOnFocusChangeListener(mFocusChangeListener);
         PendingIntent contentIntent = n.contentIntent;
         if (contentIntent != null) {
-            content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.id));
+            content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
         }
 
         View child = null;
@@ -896,7 +896,7 @@
                         com.android.internal.R.id.content);
                 PendingIntent contentIntent = n.contentIntent;
                 if (contentIntent != null) {
-                    content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.id));
+                    content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
                 }
             }
             catch (RuntimeException e) {
@@ -1248,11 +1248,13 @@
     private class Launcher implements View.OnClickListener {
         private PendingIntent mIntent;
         private String mPkg;
+        private String mTag;
         private int mId;
 
-        Launcher(PendingIntent intent, String pkg, int id) {
+        Launcher(PendingIntent intent, String pkg, String tag, int id) {
             mIntent = intent;
             mPkg = pkg;
+            mTag = tag;
             mId = id;
         }
 
@@ -1267,7 +1269,7 @@
             }
             try {
                 mIntent.send();
-                mNotificationCallbacks.onNotificationClick(mPkg, mId);
+                mNotificationCallbacks.onNotificationClick(mPkg, mTag, mId);
             } catch (PendingIntent.CanceledException e) {
                 // the stack trace isn't very helpful here.  Just log the exception message.
                 Log.w(TAG, "Sending contentIntent failed: " + e);