Checkpoint.  Data structures for Notifications in place.

Change-Id: I146fb9bc1d349112541368e2c99a667821dfdf6e
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4d72f73..739aca3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -341,6 +341,44 @@
         iconLevel = parcel.readInt();
     }
 
+    public Notification clone() {
+        Notification that = new Notification();
+
+        that.when = this.when;
+        that.icon = this.icon;
+        that.number = this.number;
+
+        // PendingIntents are global, so there's no reason (or way) to clone them.
+        that.contentIntent = this.contentIntent;
+        that.deleteIntent = this.deleteIntent;
+
+        if (this.tickerText != null) {
+            that.tickerText = this.tickerText.toString();
+        }
+        if (this.contentView != null) {
+            that.contentView = this.contentView.clone();
+        }
+        that.iconLevel = that.iconLevel;
+        that.sound = this.sound; // android.net.Uri is immutable
+        that.audioStreamType = this.audioStreamType;
+
+        final long[] vibrate = this.vibrate;
+        if (vibrate != null) {
+            final int N = vibrate.length;
+            final long[] vib = that.vibrate = new long[N];
+            System.arraycopy(vibrate, 0, vib, 0, N);
+        }
+
+        that.ledARGB = this.ledARGB;
+        that.ledOnMS = this.ledOnMS;
+        that.ledOffMS = this.ledOffMS;
+        that.defaults = this.defaults;
+        
+        that.flags = this.flags;
+
+        return that;
+    }
+
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 3003580..7a70c80 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -100,6 +100,7 @@
      * Base class for all actions that can be performed on an 
      * inflated view.
      *
+     *  SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
      */
     private abstract static class Action implements Parcelable {
         public abstract void apply(View root) throws ActionException;
@@ -568,6 +569,14 @@
         }
     }
 
+    public RemoteViews clone() {
+        final RemoteViews that = new RemoteViews(mPackage, mLayoutId);
+        if (mActions != null) {
+            that.mActions = (ArrayList<Action>)mActions.clone();
+        }
+        return that;
+    }
+
     public String getPackage() {
         return mPackage;
     }
diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java
index 455e17b..e28325c 100644
--- a/core/java/com/android/internal/statusbar/StatusBarIcon.java
+++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java
@@ -27,6 +27,7 @@
     public int iconId;
     public int iconLevel;
     public boolean visible = true;
+    public int number;
 
     private StatusBarIcon() {
     }
@@ -39,12 +40,14 @@
 
     public String toString() {
         return "StatusBarIcon(pkg=" + this.iconPackage + " id=0x" + Integer.toHexString(this.iconId)
-                + " level=" + this.iconLevel + " visible=" + visible + ")";
+                + " level=" + this.iconLevel + " visible=" + visible
+                + " num=" + this.number + " )";
     }
 
     public StatusBarIcon clone() {
         StatusBarIcon that = new StatusBarIcon(this.iconPackage, this.iconId, this.iconLevel);
         that.visible = this.visible;
+        that.number = this.number;
         return that;
     }
 
@@ -60,13 +63,14 @@
         this.iconId = in.readInt();
         this.iconLevel = in.readInt();
         this.visible = in.readInt() != 0;
+        this.number = in.readInt();
     }
 
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(this.iconPackage);
         out.writeInt(this.iconId);
         out.writeInt(this.iconLevel);
-        out.writeInt(this.visible ? 1 : 0);
+        out.writeInt(this.number);
     }
 
     public int describeContents() {
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java
new file mode 100644
index 0000000..ba1525e
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.statusbar;
+
+import android.app.Notification;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.widget.RemoteViews;
+
+
+/*
+boolean clearable = !n.ongoingEvent && ((notification.flags & Notification.FLAG_NO_CLEAR) == 0);
+
+
+// TODO: make this restriction do something smarter like never fill
+// more than two screens.  "Why would anyone need more than 80 characters." :-/
+final int maxTickerLen = 80;
+if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) {
+    truncatedTicker = truncatedTicker.subSequence(0, maxTickerLen);
+}
+*/
+
+public class StatusBarNotification implements Parcelable {
+    public String pkg;
+    public int id;
+    public String tag;
+    Notification notification;
+
+    public StatusBarNotification() {
+    }
+
+    public StatusBarNotification(String pkg, int id, String tag, Notification notification) {
+        if (pkg == null) throw new NullPointerException();
+        if (notification == null) throw new NullPointerException();
+
+        this.pkg = pkg;
+        this.id = id;
+        this.tag = tag;
+        this.notification = notification;
+    }
+
+    public StatusBarNotification(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public void readFromParcel(Parcel in) {
+        this.pkg = in.readString();
+        this.id = in.readInt();
+        if (in.readInt() != 0) {
+            this.tag = in.readString();
+        } else {
+            this.tag = null;
+        }
+        this.notification = new Notification(in);
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(this.pkg);
+        out.writeInt(this.id);
+        if (this.tag != null) {
+            out.writeInt(1);
+            out.writeString(this.tag);
+        } else {
+            out.writeInt(0);
+        }
+        this.notification.writeToParcel(out, flags);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<StatusBarNotification> CREATOR
+            = new Parcelable.Creator<StatusBarNotification>()
+    {
+        public StatusBarNotification createFromParcel(Parcel parcel)
+        {
+            return new StatusBarNotification(parcel);
+        }
+
+        public StatusBarNotification[] newArray(int size)
+        {
+            return new StatusBarNotification[size];
+        }
+    };
+
+    public StatusBarNotification clone() {
+        return new StatusBarNotification(this.pkg, this.id, this.tag, this.notification.clone());
+    }
+
+    public String toString() {
+        return "StatusBarNotification(package=" + pkg + " tag=" + tag
+                + " notification=" + notification + ")";
+    }
+
+}
+
+
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotificationList.aidl b/core/java/com/android/internal/statusbar/StatusBarNotificationList.aidl
new file mode 100644
index 0000000..10abeee
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/StatusBarNotificationList.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package com.android.internal.statusbar;
+
+parcelable StatusBarNotificationList;
+
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotificationList.java b/core/java/com/android/internal/statusbar/StatusBarNotificationList.java
new file mode 100644
index 0000000..2b70f5f
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/StatusBarNotificationList.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.statusbar;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class StatusBarNotificationList implements Parcelable {
+    private class Entry {
+        IBinder key;
+        public StatusBarNotification notification;
+
+        void writeToParcel(Parcel out, int flags) {
+            out.writeStrongBinder(key);
+            notification.writeToParcel(out, flags);
+        }
+
+        void readFromParcel(Parcel in) {
+            key = in.readStrongBinder();
+            notification = new StatusBarNotification(in);
+        }
+
+        public Entry clone() {
+            Entry that = new Entry();
+            that.key = this.key;
+            that.notification = this.notification.clone();
+            return that;
+        }
+    }
+
+    private ArrayList<Entry> mEntries = new ArrayList<Entry>();
+
+    public StatusBarNotificationList() {
+    }
+
+    public StatusBarNotificationList(Parcel in) {
+        readFromParcel(in);
+    }
+    
+    public void readFromParcel(Parcel in) {
+        final int N = in.readInt();
+        for (int i=0; i<N; i++) {
+            Entry e = new Entry();
+            e.readFromParcel(in);
+            mEntries.add(e);
+        }
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        final int N = mEntries.size();
+        out.writeInt(N);
+        for (int i=0; i<N; i++) {
+            mEntries.get(i).writeToParcel(out, flags);
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Parcelable.Creator that instantiates StatusBarNotificationList objects
+     */
+    public static final Parcelable.Creator<StatusBarNotificationList> CREATOR
+            = new Parcelable.Creator<StatusBarNotificationList>()
+    {
+        public StatusBarNotificationList createFromParcel(Parcel parcel)
+        {
+            return new StatusBarNotificationList(parcel);
+        }
+
+        public StatusBarNotificationList[] newArray(int size)
+        {
+            return new StatusBarNotificationList[size];
+        }
+    };
+
+    public void copyFrom(StatusBarNotificationList that) {
+        mEntries.clear();
+        final int N = that.mEntries.size();
+        for (int i=0; i<N; i++) {
+            mEntries.add(that.mEntries.get(i).clone());
+        }
+    }
+
+    public void dump(PrintWriter pw) {
+        final int N = mEntries.size();
+        pw.println("Notification list:");
+        for (int i=0; i<N; i++) {
+            Entry e = mEntries.get(i);
+            pw.printf("  %2d: %s\n", i, e.notification.toString());
+        }
+    }
+
+
+    public int size() {
+        return mEntries.size();
+    }
+
+    public IBinder add(StatusBarNotification notification) {
+        if (notification == null) throw new NullPointerException();
+
+        Entry entry = new Entry();
+        entry.key = new Binder();
+        entry.notification = notification.clone();
+
+        // TODO: Sort correctly by "when"
+        mEntries.add(entry);
+
+        return entry.key;
+    }
+
+    public void update(IBinder key, StatusBarNotification notification) {
+        final int index = getIndex(key);
+        if (index < 0) {
+            throw new IllegalArgumentException("got invalid key: " + key);
+        }
+        final Entry entry = mEntries.get(index);
+        entry.notification = notification.clone();
+    }
+
+    public void remove(IBinder key) {
+        final int index = getIndex(key);
+        if (index < 0) {
+            throw new IllegalArgumentException("got invalid key: " + key);
+        }
+        mEntries.remove(index);
+    }
+
+    public int getIndex(IBinder key) {
+        final ArrayList<Entry> entries = mEntries;
+        final int N = entries.size();
+        for (int i=0; i<N; i++) {
+            if (entries.get(i).key == key) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public StatusBarNotification getNotification(int index) {
+        return mEntries.get(index).notification;
+    }
+}
+
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java
index bc02625..1ae3585 100644
--- a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java
@@ -426,6 +426,11 @@
         return row;
     }
 
+    /*
+                StatusBarIcon icon = new StatusBarIcon(pkg, notification.icon,
+                        notification.iconLevel);
+                icon.number = notification.number;
+    */     
     void addNotificationView(StatusBarNotification notification) {
         if (notification.view != null) {
             throw new RuntimeException("Assertion failed: notification.view="
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarIconView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarIconView.java
index 874fcfa..58b9822 100644
--- a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarIconView.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarIconView.java
@@ -61,6 +61,7 @@
                 && mIcon.visible == icon.visible;
         if (!iconEquals) {
             setImageDrawable(getIcon(icon));
+            // TODO: What if getIcon returns null?
         }
         if (!levelEquals) {
             setImageLevel(icon.iconLevel);
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarService.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarService.java
index d783c11..20f3552 100644
--- a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarService.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarService.java
@@ -37,8 +37,6 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarIconList;
 
-import com.android.server.status.NotificationData;
-
 public abstract class StatusBarService extends Service implements CommandQueue.Callbacks {
     private static final String TAG = "StatusBarService";
 
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 327e18b..d3fbd6d 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -17,7 +17,7 @@
 package com.android.server;
 
 import com.android.server.status.IconData;
-import com.android.server.status.NotificationData;
+import com.android.internal.statusbar.StatusBarNotification;
 import com.android.server.status.StatusBarManagerService;
 
 import android.app.ActivityManagerNative;
@@ -705,36 +705,12 @@
             }
 
             if (notification.icon != 0) {
-                IconData icon = IconData.makeIcon(null, pkg, notification.icon,
-                                                    notification.iconLevel,
-                                                    notification.number);
-                CharSequence truncatedTicker = notification.tickerText;
-
-                // TODO: make this restriction do something smarter like never fill
-                // more than two screens.  "Why would anyone need more than 80 characters." :-/
-                final int maxTickerLen = 80;
-                if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) {
-                    truncatedTicker = truncatedTicker.subSequence(0, maxTickerLen);
-                }
-
-                NotificationData n = new NotificationData();
-                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;
+                StatusBarNotification n = new StatusBarNotification(pkg, id, tag, notification);
                 if (old != null && old.statusBarKey != null) {
                     r.statusBarKey = old.statusBarKey;
                     long identity = Binder.clearCallingIdentity();
                     try {
-                        mStatusBar.updateNotification(r.statusBarKey, icon, n);
+                        mStatusBar.updateNotification(r.statusBarKey, n);
                     }
                     finally {
                         Binder.restoreCallingIdentity(identity);
@@ -742,16 +718,14 @@
                 } else {
                     long identity = Binder.clearCallingIdentity();
                     try {
-                        r.statusBarKey = mStatusBar.addNotification(icon, n);
+                        r.statusBarKey = mStatusBar.addNotification(n);
                         mAttentionLight.pulse();
                     }
                     finally {
                         Binder.restoreCallingIdentity(identity);
                     }
                 }
-
                 sendAccessibilityEvent(notification, pkg);
-
             } else {
                 if (old != null && old.statusBarKey != null) {
                     long identity = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/status/NotificationData.java b/services/java/com/android/server/status/NotificationData.java
deleted file mode 100644
index 71f01ca..0000000
--- a/services/java/com/android/server/status/NotificationData.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-import android.app.PendingIntent;
-import android.widget.RemoteViews;
-
-public class NotificationData {
-    public String pkg;
-    public String tag;
-    public int id;
-    public CharSequence tickerText;
-
-    public long when;
-    public boolean ongoingEvent;
-    public boolean clearable;
-
-    public RemoteViews contentView;
-    public PendingIntent contentIntent;
-
-    public PendingIntent deleteIntent;
-
-    public String toString() {
-        return "NotificationData(package=" + pkg + " id=" + id + " tickerText=" + tickerText
-                + " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent
-                + " deleteIntent=" + deleteIntent
-                + " clearable=" + clearable
-                + " contentView=" + contentView + " when=" + when + ")";
-    }
-}
diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java
deleted file mode 100644
index e18e9dd..0000000
--- a/services/java/com/android/server/status/NotificationViewList.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-import android.os.IBinder;
-import android.util.Slog;
-import android.view.View;
-import java.util.ArrayList;
-
-public class NotificationViewList {
-    private ArrayList<StatusBarNotification> mOngoing = new ArrayList();
-    private ArrayList<StatusBarNotification> mLatest = new ArrayList();
-
-    public NotificationViewList() {
-    }
-
-    private static final int indexInList(ArrayList<StatusBarNotification> list, NotificationData n){
-        final int N = list.size();
-        for (int i=0; i<N; i++) {
-            StatusBarNotification that = list.get(i);
-            if (that.data == n) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    int getIconIndex(NotificationData n) {
-        final int ongoingSize = mOngoing.size();
-        final int latestSize = mLatest.size();
-        if (n.ongoingEvent) {
-            int index = indexInList(mOngoing, n);
-            if (index >= 0) {
-                return latestSize + index + 1;
-            } else {
-                return -1;
-            }
-        } else {
-            return indexInList(mLatest, n) + 1;
-        }
-    }
-
-    void remove(StatusBarNotification notification) {
-        NotificationData n = notification.data;
-        int index;
-        index = indexInList(mOngoing, n);
-        if (index >= 0) {
-            mOngoing.remove(index);
-            return;
-        }
-        index = indexInList(mLatest, n);
-        if (index >= 0) {
-            mLatest.remove(index);
-            return;
-        }
-    }
-
-    ArrayList<StatusBarNotification> notificationsForPackage(String packageName) {
-        ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>();
-        int N = mOngoing.size();
-        for (int i=0; i<N; i++) {
-            if (matchPackage(mOngoing.get(i), packageName)) {
-                list.add(mOngoing.get(i));
-            }
-        }
-        N = mLatest.size();
-        for (int i=0; i<N; i++) {
-            if (matchPackage(mLatest.get(i), packageName)) {
-                list.add(mLatest.get(i));
-            }
-        }
-        return list;
-    }
-    
-    private final boolean matchPackage(StatusBarNotification snb, String packageName) {
-        if (snb.data.contentIntent != null) {
-            if (snb.data.contentIntent.getTargetPackage().equals(packageName)) {
-                return true;
-            }
-        } else if (snb.data.pkg != null && snb.data.pkg.equals(packageName)) {
-            return true;
-        }
-        return false;
-    }
-    
-    private static final int indexForKey(ArrayList<StatusBarNotification> list, IBinder key) {
-        final int N = list.size();
-        for (int i=0; i<N; i++) {
-            if (list.get(i).key == key) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    StatusBarNotification get(IBinder key) {
-        int index;
-        index = indexForKey(mOngoing, key);
-        if (index >= 0) {
-            return mOngoing.get(index);
-        }
-        index = indexForKey(mLatest, key);
-        if (index >= 0) {
-            return mLatest.get(index);
-        }
-        return null;
-    }
-
-    // gets the index of the notification's view in its expanded parent view
-    int getExpandedIndex(StatusBarNotification notification) {
-        ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
-        final IBinder key = notification.key;
-        int index = 0;
-        // (the view order is backwards from this list order)
-        for (int i=list.size()-1; i>=0; i--) {
-            StatusBarNotification item = list.get(i);
-            if (item.key == key) {
-                return index;
-            }
-            if (item.view != null) {
-                index++;
-            }
-        }
-        Slog.e(StatusBarManagerService.TAG, "Couldn't find notification in NotificationViewList.");
-        Slog.e(StatusBarManagerService.TAG, "notification=" + notification);
-        dump(notification);
-        return 0;
-    }
-
-    void clearViews() {
-        int N = mOngoing.size();
-        for (int i=0; i<N; i++) {
-            mOngoing.get(i).view = null;
-        }
-        N = mLatest.size();
-        for (int i=0; i<N; i++) {
-            mLatest.get(i).view = null;
-        }
-    }
-    
-    int ongoingCount() {
-        return mOngoing.size();
-    }
-
-    int latestCount() {
-        return mLatest.size();
-    }
-
-    StatusBarNotification getOngoing(int index) {
-        return mOngoing.get(index);
-    }
-
-    StatusBarNotification getLatest(int index) {
-        return mLatest.get(index);
-    }
-
-    int size() {
-        return mOngoing.size() + mLatest.size();
-    }
-
-    void add(StatusBarNotification notification) {
-        if (StatusBarManagerService.SPEW) {
-            Slog.d(StatusBarManagerService.TAG, "before add NotificationViewList"
-                    + " notification.data.ongoingEvent=" + notification.data.ongoingEvent);
-            dump(notification);
-        }
-
-        ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
-        long when = notification.data.when;
-        final int N = list.size();
-        int index = N;
-        for (int i=0; i<N; i++) {
-            StatusBarNotification that = list.get(i);
-            if (that.data.when > when) {
-                index = i;
-                break;
-            }
-        }
-        list.add(index, notification);
-
-        if (StatusBarManagerService.SPEW) {
-            Slog.d(StatusBarManagerService.TAG, "after add NotificationViewList index=" + index);
-            dump(notification);
-        }
-    }
-
-    void dump(StatusBarNotification notification) {
-        if (StatusBarManagerService.SPEW) {
-            boolean showTime = false;
-            String s = "";
-            for (int i=0; i<mOngoing.size(); i++) {
-                StatusBarNotification that = mOngoing.get(i);
-                if (that.key == notification.key) {
-                    s += "[";
-                }
-                if (showTime) {
-                    s += that.data.when;
-                } else {
-                    s += that.data.pkg + "/" + that.data.id + "/" + that.view;
-                }
-                if (that.key == notification.key) {
-                    s += "]";
-                }
-                s += " ";
-            }
-            Slog.d(StatusBarManagerService.TAG, "NotificationViewList ongoing: " + s);
-
-            s = "";
-            for (int i=0; i<mLatest.size(); i++) {
-                StatusBarNotification that = mLatest.get(i);
-                if (that.key == notification.key) {
-                    s += "[";
-                }
-                if (showTime) {
-                    s += that.data.when;
-                } else {
-                    s += that.data.pkg + "/" + that.data.id + "/" + that.view;
-                }
-                if (that.key == notification.key) {
-                    s += "]";
-                }
-                s += " ";
-            }
-            Slog.d(StatusBarManagerService.TAG, "NotificationViewList latest:  " + s);
-        }
-    }
-
-    StatusBarNotification get(View view) {
-        int N = mOngoing.size();
-        for (int i=0; i<N; i++) {
-            StatusBarNotification notification = mOngoing.get(i);
-            View v = notification.view;
-            if (v == view) {
-                return notification;
-            }
-        }
-        N = mLatest.size();
-        for (int i=0; i<N; i++) {
-            StatusBarNotification notification = mLatest.get(i);
-            View v = notification.view;
-            if (v == view) {
-                return notification;
-            }
-        }
-        return null;
-    }
-
-    void update(StatusBarNotification notification) {
-        remove(notification);
-        add(notification);
-    }
-
-    boolean hasClearableItems() {
-        int N = mLatest.size();
-        for (int i=0; i<N; i++) {
-            if (mLatest.get(i).data.clearable) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/services/java/com/android/server/status/StatusBarManagerService.java b/services/java/com/android/server/status/StatusBarManagerService.java
index a59b914..f4678c37 100644
--- a/services/java/com/android/server/status/StatusBarManagerService.java
+++ b/services/java/com/android/server/status/StatusBarManagerService.java
@@ -36,6 +36,8 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.statusbar.StatusBarNotification;
+import com.android.internal.statusbar.StatusBarNotificationList;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -60,10 +62,7 @@
     NotificationCallbacks mNotificationCallbacks;
     volatile IStatusBar mBar;
     StatusBarIconList mIcons = new StatusBarIconList();
-    private UninstallReceiver mUninstallReceiver;
-
-    // expanded notifications
-    NotificationViewList mNotificationData = new NotificationViewList();
+    StatusBarNotificationList mNotifications = new StatusBarNotificationList();
 
     // for disabling the status bar
     ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
@@ -93,7 +92,6 @@
      */
     public StatusBarManagerService(Context context) {
         mContext = context;
-        mUninstallReceiver = new UninstallReceiver();
 
         final Resources res = context.getResources();
         mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.status_bar_icon_order));
@@ -266,15 +264,29 @@
         Slog.d(TAG, "visibilityChanged visible=" + visible);
     }
 
-    public IBinder addNotification(IconData iconData, NotificationData notificationData) {
-        return new Binder();
+    // ================================================================================
+    // Callbacks for NotificationManagerService.
+    // ================================================================================
+    public IBinder addNotification(StatusBarNotification notification) {
+        synchronized (mNotifications) {
+            IBinder key = mNotifications.add(notification);
+            // TODO: tell mBar
+            return key;
+        }
     }
 
-    public void updateNotification(IBinder key, IconData iconData,
-            NotificationData notificationData) {
+    public void updateNotification(IBinder key, StatusBarNotification notification) {
+        synchronized (mNotifications) {
+            mNotifications.update(key, notification);
+            // TODO: tell mBar
+        }
     }
 
     public void removeNotification(IBinder key) {
+        synchronized (mNotifications) {
+            mNotifications.remove(key);
+            // TODO: tell mBar
+        }
     }
 
     // ================================================================================
@@ -336,12 +348,6 @@
     // Always called from UI thread
     // ================================================================================
 
-    StatusBarNotification getNotification(IBinder key) {
-        synchronized (mNotificationData) {
-            return mNotificationData.get(key);
-        }
-    }
-
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -354,23 +360,11 @@
         synchronized (mIcons) {
             mIcons.dump(pw);
         }
-        
-        synchronized (mNotificationData) {
-            int N = mNotificationData.ongoingCount();
-            pw.println("  ongoingCount.size=" + N);
-            for (int i=0; i<N; i++) {
-                StatusBarNotification n = mNotificationData.getOngoing(i);
-                pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
-                pw.println("           data=" + n.data);
-            }
-            N = mNotificationData.latestCount();
-            pw.println("  ongoingCount.size=" + N);
-            for (int i=0; i<N; i++) {
-                StatusBarNotification n = mNotificationData.getLatest(i);
-                pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
-                pw.println("           data=" + n.data);
-            }
+
+        synchronized (mNotifications) {
+            mNotifications.dump(pw);
         }
+
         synchronized (mDisableRecords) {
             final int N = mDisableRecords.size();
             pw.println("  mDisableRecords.size=" + N
@@ -422,47 +416,4 @@
         }
     };
 
-
-    class UninstallReceiver extends BroadcastReceiver {
-        public UninstallReceiver() {
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-            filter.addDataScheme("package");
-            mContext.registerReceiver(this, filter);
-            IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-            mContext.registerReceiver(this, sdFilter);
-        }
-        
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String pkgList[] = null;
-            if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
-                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            } else {
-                Uri data = intent.getData();
-                if (data != null) {
-                    String pkg = data.getSchemeSpecificPart();
-                    if (pkg != null) {
-                        pkgList = new String[]{pkg};
-                    }
-                }
-            }
-            ArrayList<StatusBarNotification> list = null;
-            if (pkgList != null) {
-                synchronized (StatusBarManagerService.this) {
-                    for (String pkg : pkgList) {
-                        list = mNotificationData.notificationsForPackage(pkg);
-                    }
-                }
-            }
-            
-            if (list != null) {
-                final int N = list.size();
-                for (int i=0; i<N; i++) {
-                    // TODO: removeIcon(list.get(i).key);
-                }
-            }
-        }
-    }
 }
diff --git a/services/java/com/android/server/status/StatusBarNotification.java b/services/java/com/android/server/status/StatusBarNotification.java
deleted file mode 100644
index 4bb8fdb..0000000
--- a/services/java/com/android/server/status/StatusBarNotification.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-import android.os.IBinder;
-import android.view.View;
-
-public class StatusBarNotification {
-    IBinder key;
-    NotificationData data;
-    View view;
-    View contentView;
-}