Checkpoint status bar factoring.  Now it builds and doesn't crash at boot.

Change-Id: I23f2045abfec0b414d5381f5e609b7267da7f21a
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1e007d36..3434dca9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1567,23 +1567,6 @@
     <!-- A format string for 12-hour time of day, just the hour, not the minute, with capital "AM" or "PM" (example: "3PM"). -->
     <string name="hour_cap_ampm">"<xliff:g id="hour" example="3">%-l</xliff:g><xliff:g id="ampm" example="PM">%p</xliff:g>"</string>
 
-    <!-- The text for the button in the notification window-shade that clears
-         all of the currently visible notifications. -->
-    <string name="status_bar_clear_all_button">Clear</string>
-
-    <!-- The label in the bar at the top of the status bar when there are no notifications
-         showing. -->
-    <string name="status_bar_no_notifications_title">No notifications</string>
-
-    <!-- The label for the group of notifications for ongoing events in the opened version of
-         the status bar.  An ongoing call is the prime example of this.  The MP3 music player
-         might be another example.  -->
-    <string name="status_bar_ongoing_events_title">Ongoing</string>
-
-    <!-- The label for the group of notifications for recent events in the opened version of
-         the status bar.  Recently received text messsages (SMS), emails, calendar alerts, etc. -->
-    <string name="status_bar_latest_events_title">Notifications</string>
-
     <!-- The big percent text in the middle of the battery icon that appears when you plug in
          the charger. -->
     <string name="battery_status_text_percent_format"><xliff:g id="number" example="50">%d</xliff:g><xliff:g id="percent" example="%">%%</xliff:g></string>
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal.9.png b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal.9.png
new file mode 100644
index 0000000..baafed6
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable.9.png b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable.9.png
new file mode 100644
index 0000000..175197b
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
new file mode 100644
index 0000000..ec1feff
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_pressed.9.png b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_pressed.9.png
new file mode 100644
index 0000000..c1f9a0f
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_pressed.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_selected.9.png b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_selected.9.png
new file mode 100644
index 0000000..0ea3f40
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_selected.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/stat_notify_more.png b/packages/StatusBarPhone/res/drawable-hdpi/stat_notify_more.png
new file mode 100755
index 0000000..1c7f9db
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/stat_notify_more.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/status_bar_close_on.9.png b/packages/StatusBarPhone/res/drawable-hdpi/status_bar_close_on.9.png
new file mode 100644
index 0000000..5acf638
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/status_bar_close_on.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/status_bar_header_background.9.png b/packages/StatusBarPhone/res/drawable-hdpi/status_bar_header_background.9.png
new file mode 100644
index 0000000..be36ff2
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/status_bar_header_background.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/statusbar_background.9.png b/packages/StatusBarPhone/res/drawable-hdpi/statusbar_background.9.png
new file mode 100644
index 0000000..dcca695
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/statusbar_background.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/title_bar_portrait.9.png b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_portrait.9.png
new file mode 100644
index 0000000..70f7cc2
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_portrait.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/title_bar_shadow.9.png b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_shadow.9.png
new file mode 100644
index 0000000..e6dab63
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal.9.png b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal.9.png
new file mode 100644
index 0000000..bcedd5f
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable.9.png b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable.9.png
new file mode 100644
index 0000000..ac6260f
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
new file mode 100644
index 0000000..4ee1b3f
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_pressed.9.png b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_pressed.9.png
new file mode 100644
index 0000000..25e38f4
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_pressed.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_selected.9.png b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_selected.9.png
new file mode 100644
index 0000000..cc209c6
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_selected.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/stat_notify_more.png b/packages/StatusBarPhone/res/drawable-mdpi/stat_notify_more.png
new file mode 100644
index 0000000..e129ba9
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/stat_notify_more.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/status_bar_close_on.9.png b/packages/StatusBarPhone/res/drawable-mdpi/status_bar_close_on.9.png
new file mode 100644
index 0000000..9cbd9fe
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/status_bar_close_on.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/status_bar_header_background.9.png b/packages/StatusBarPhone/res/drawable-mdpi/status_bar_header_background.9.png
new file mode 100644
index 0000000..fa9a90c
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/status_bar_header_background.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/statusbar_background.9.png b/packages/StatusBarPhone/res/drawable-mdpi/statusbar_background.9.png
new file mode 100644
index 0000000..eb7c1a4
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/statusbar_background.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/title_bar_portrait.9.png b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_portrait.9.png
new file mode 100644
index 0000000..13b18d8
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_portrait.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/title_bar_shadow.9.png b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_shadow.9.png
new file mode 100644
index 0000000..dbcefee
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable/btn_default_small.xml b/packages/StatusBarPhone/res/drawable/btn_default_small.xml
new file mode 100644
index 0000000..5485ea0
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable/btn_default_small.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_default_small_normal" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_default_small_normal_disable" />
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/btn_default_small_pressed" />
+    <item android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_default_small_selected" />
+    <item android:state_enabled="true"
+        android:drawable="@drawable/btn_default_small_normal" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_default_small_normal_disable_focused" />
+    <item
+         android:drawable="@drawable/btn_default_small_normal_disable" />
+</selector>
+
diff --git a/core/res/res/layout/status_bar.xml b/packages/StatusBarPhone/res/layout/status_bar.xml
similarity index 100%
rename from core/res/res/layout/status_bar.xml
rename to packages/StatusBarPhone/res/layout/status_bar.xml
diff --git a/packages/StatusBarPhone/res/layout/status_bar_expanded.xml b/packages/StatusBarPhone/res/layout/status_bar_expanded.xml
new file mode 100644
index 0000000..30138a7
--- /dev/null
+++ b/packages/StatusBarPhone/res/layout/status_bar_expanded.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<com.android.server.status.ExpandedView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:focusable="true"
+    android:descendantFocusability="afterDescendants"
+    >
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingTop="3dp"
+        android:paddingBottom="5dp"
+        android:paddingRight="3dp"
+        android:background="@drawable/status_bar_header_background"
+        >
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_marginTop="1dp"
+            android:layout_marginLeft="5dp"
+            android:layout_gravity="center_vertical"
+            android:paddingBottom="1dp"
+            android:orientation="vertical"
+            >
+                <TextView android:id="@+id/plmnLabel"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_vertical"
+                    android:textAppearance="?android:attr/textAppearanceLarge"
+                    android:textColor="?android:attr/textColorSecondaryInverse"
+                    android:paddingLeft="4dp"
+                    />
+                <TextView android:id="@+id/spnLabel"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_vertical"
+                    android:textAppearance="?android:attr/textAppearanceLarge"
+                    android:textColor="?android:attr/textColorSecondaryInverse"
+                    android:paddingLeft="4dp"
+                    />
+        </LinearLayout>
+        <TextView android:id="@+id/clear_all_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginTop="4dp"
+            android:layout_marginBottom="1dp"
+            android:textSize="14sp"
+            android:textColor="#ff000000"
+            android:text="@string/status_bar_clear_all_button"
+            style="?android:attr/buttonStyle"
+            android:paddingLeft="15dp"
+            android:paddingRight="15dp"
+            android:background="@drawable/btn_default_small"
+            />
+    </LinearLayout>
+
+    <FrameLayout
+        android:layout_width="match_parent" 
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        >
+        <ScrollView
+            android:id="@+id/scroll"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:fadingEdge="none"
+            >
+            <com.android.server.status.NotificationLinearLayout
+                android:id="@+id/notificationLinearLayout"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                >
+                
+                <TextView android:id="@+id/noNotificationsTitle"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/title_bar_portrait"
+                    android:paddingLeft="5dp"
+                    android:textAppearance="@style/TextAppearance.StatusBarTitle"
+                    android:text="@string/status_bar_no_notifications_title"
+                    />
+
+                <TextView android:id="@+id/ongoingTitle"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/title_bar_portrait"
+                    android:paddingLeft="5dp"
+                    android:textAppearance="@style/TextAppearance.StatusBarTitle"
+                    android:text="@string/status_bar_ongoing_events_title"
+                    />
+                <LinearLayout android:id="@+id/ongoingItems"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="vertical"
+                    />
+
+                <TextView android:id="@+id/latestTitle"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/title_bar_portrait"
+                    android:paddingLeft="5dp"
+                    android:textAppearance="@style/TextAppearance.StatusBarTitle"
+                    android:text="@string/status_bar_latest_events_title"
+                    />
+                <LinearLayout android:id="@+id/latestItems"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="vertical"
+                    />
+            </com.android.server.status.NotificationLinearLayout>
+        </ScrollView>
+
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:src="@drawable/title_bar_shadow"
+            android:scaleType="fitXY"
+        />
+
+    </FrameLayout>
+</com.android.server.status.ExpandedView>
diff --git a/packages/StatusBarPhone/res/layout/status_bar_tracking.xml b/packages/StatusBarPhone/res/layout/status_bar_tracking.xml
new file mode 100644
index 0000000..c0a7a97
--- /dev/null
+++ b/packages/StatusBarPhone/res/layout/status_bar_tracking.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+-->
+
+<com.android.server.status.TrackingView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:visibility="gone"
+    android:focusable="true"
+    android:descendantFocusability="afterDescendants"
+    android:paddingBottom="0px"
+    android:paddingLeft="0px"
+    android:paddingRight="0px"
+    >
+
+    <com.android.server.status.TrackingPatternView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        />
+
+    <com.android.server.status.CloseDragHandle android:id="@+id/close"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        >
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:scaleType="fitXY"
+            android:src="@drawable/status_bar_close_on"/>
+
+    </com.android.server.status.CloseDragHandle>
+
+</com.android.server.status.TrackingView>
diff --git a/packages/StatusBarPhone/res/values/arrays.xml b/packages/StatusBarPhone/res/values/arrays.xml
new file mode 100644
index 0000000..9076a47
--- /dev/null
+++ b/packages/StatusBarPhone/res/values/arrays.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Do not translate. Defines the slots for the right-hand side icons.  That is to say, the
+         icons in the status bar that are not notifications. -->
+    <string-array name="status_bar_icon_order">
+        <item><xliff:g id="id">clock</xliff:g></item>
+        <item><xliff:g id="id">secure</xliff:g></item>
+        <item><xliff:g id="id">alarm_clock</xliff:g></item>
+        <item><xliff:g id="id">battery</xliff:g></item>
+        <item><xliff:g id="id">phone_signal</xliff:g></item>
+        <item><xliff:g id="id">phone_evdo_signal</xliff:g></item>
+        <item><xliff:g id="id">data_connection</xliff:g></item>
+        <item><xliff:g id="id">cdma_eri</xliff:g></item>
+        <item><xliff:g id="id">tty</xliff:g></item>
+        <item><xliff:g id="id">volume</xliff:g></item>
+        <item><xliff:g id="id">mute</xliff:g></item>
+        <item><xliff:g id="id">speakerphone</xliff:g></item>
+        <item><xliff:g id="id">wifi</xliff:g></item>
+        <item><xliff:g id="id">tty</xliff:g></item>
+        <item><xliff:g id="id">bluetooth</xliff:g></item>
+        <item><xliff:g id="id">gps</xliff:g></item>
+        <item><xliff:g id="id">sync_active</xliff:g></item>
+        <item><xliff:g id="id">sync_failing</xliff:g></item>
+        <item><xliff:g id="id">ime</xliff:g></item>
+    </string-array>
+
+</resources>
diff --git a/packages/StatusBarPhone/res/values/dimens.xml b/packages/StatusBarPhone/res/values/dimens.xml
new file mode 100644
index 0000000..93cf377
--- /dev/null
+++ b/packages/StatusBarPhone/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2006, 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.
+*/
+-->
+<resources>
+    <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
+    <dimen name="status_bar_edge_ignore">5dp</dimen>
+</resources>
+
diff --git a/packages/StatusBarPhone/res/values/strings.xml b/packages/StatusBarPhone/res/values/strings.xml
index 40ac66a..8e42b59 100644
--- a/packages/StatusBarPhone/res/values/strings.xml
+++ b/packages/StatusBarPhone/res/values/strings.xml
@@ -19,4 +19,22 @@
 <resources>
     <!-- Name of the status bar as seen in the applications info settings page. -->
     <string name="app_label">Status Bar</string>
+
+    <!-- The text for the button in the notification window-shade that clears
+         all of the currently visible notifications. -->
+    <string name="status_bar_clear_all_button">Clear</string>
+
+    <!-- The label in the bar at the top of the status bar when there are no notifications
+         showing. -->
+    <string name="status_bar_no_notifications_title">No notifications</string>
+
+    <!-- The label for the group of notifications for ongoing events in the opened version of
+         the status bar.  An ongoing call is the prime example of this.  The MP3 music player
+         might be another example.  -->
+    <string name="status_bar_ongoing_events_title">Ongoing</string>
+
+    <!-- The label for the group of notifications for recent events in the opened version of
+         the status bar.  Recently received text messsages (SMS), emails, calendar alerts, etc. -->
+    <string name="status_bar_latest_events_title">Notifications</string>
+
 </resources>
diff --git a/packages/StatusBarPhone/res/values/styles.xml b/packages/StatusBarPhone/res/values/styles.xml
new file mode 100644
index 0000000..41c8ae7
--- /dev/null
+++ b/packages/StatusBarPhone/res/values/styles.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <style name="TextAppearance.StatusBarTitle" parent="@android:style/TextAppearance">
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:textStyle">bold</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+
+
+</resources>
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/AnimatedImageView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/AnimatedImageView.java
new file mode 100644
index 0000000..3411f29
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/AnimatedImageView.java
@@ -0,0 +1,85 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RemoteViews.RemoteView;
+
+@RemoteView
+public class AnimatedImageView extends ImageView {
+    AnimationDrawable mAnim;
+    boolean mAttached;
+
+    public AnimatedImageView(Context context) {
+        super(context);
+    }
+
+    public AnimatedImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    private void updateAnim() {
+        Drawable drawable = getDrawable();
+        if (mAttached && mAnim != null) {
+            mAnim.stop();
+        }
+        if (drawable instanceof AnimationDrawable) {
+            mAnim = (AnimationDrawable)drawable;
+            if (mAttached) {
+                mAnim.start();
+            }
+        } else {
+            mAnim = null;
+        }
+    }
+
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+        super.setImageDrawable(drawable);
+        updateAnim();
+    }
+
+    @Override
+    @android.view.RemotableViewMethod
+    public void setImageResource(int resid) {
+        super.setImageResource(resid);
+        updateAnim();
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mAnim != null) {
+            mAnim.start();
+        }
+        mAttached = true;
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mAnim != null) {
+            mAnim.stop();
+        }
+        mAttached = false;
+    }
+}
+
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/CloseDragHandle.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/CloseDragHandle.java
new file mode 100644
index 0000000..324c145
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/CloseDragHandle.java
@@ -0,0 +1,51 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+
+
+public class CloseDragHandle extends LinearLayout {
+    PhoneStatusBarService mService;
+
+    public CloseDragHandle(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /**
+     * Ensure that, if there is no target under us to receive the touch,
+     * that we process it ourself.  This makes sure that onInterceptTouchEvent()
+     * is always called for the entire gesture.
+     */
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (event.getAction() != MotionEvent.ACTION_DOWN) {
+            mService.interceptTouchEvent(event);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        return mService.interceptTouchEvent(event)
+                ? true : super.onInterceptTouchEvent(event);
+    }
+}
+
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/DateView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/DateView.java
new file mode 100644
index 0000000..e36b124
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/DateView.java
@@ -0,0 +1,89 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.widget.TextView;
+import android.view.MotionEvent;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+public final class DateView extends TextView {
+    private static final String TAG = "DateView";
+
+    private boolean mUpdating = false;
+
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_TIME_TICK)
+                    || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
+                updateClock();
+            }
+        }
+    };
+
+    public DateView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+    }
+    
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        setUpdates(false);
+    }
+
+    @Override
+    protected int getSuggestedMinimumWidth() {
+        // makes the large background bitmap not force us to full width
+        return 0;
+    }
+
+    private final void updateClock() {
+        Date now = new Date();
+        setText(DateFormat.getDateInstance(DateFormat.LONG).format(now));
+    }
+
+    void setUpdates(boolean update) {
+        if (update != mUpdating) {
+            mUpdating = update;
+            if (update) {
+                // Register for Intent broadcasts for the clock and battery
+                IntentFilter filter = new IntentFilter();
+                filter.addAction(Intent.ACTION_TIME_TICK);
+                filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+                mContext.registerReceiver(mIntentReceiver, filter, null, null);
+                updateClock();
+            } else {
+                mContext.unregisterReceiver(mIntentReceiver);
+            }
+        }
+    }
+}
+
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/ExpandedView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/ExpandedView.java
new file mode 100644
index 0000000..6b357e8
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/ExpandedView.java
@@ -0,0 +1,59 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.util.Slog;
+
+
+public class ExpandedView extends LinearLayout {
+    PhoneStatusBarService mService;
+    int mPrevHeight = -1;
+
+    public ExpandedView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+    }
+
+    /** We want to shrink down to 0, and ignore the background. */
+    @Override
+    public int getSuggestedMinimumHeight() {
+        return 0;
+    }
+
+    @Override
+     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+         super.onLayout(changed, left, top, right, bottom);
+         int height = bottom - top;
+         if (height != mPrevHeight) {
+             //Slog.d(PhoneStatusBarService.TAG, "height changed old=" + mPrevHeight
+             //     + " new=" + height);
+             mPrevHeight = height;
+             mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
+         }
+     }
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/FixedSizeDrawable.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/FixedSizeDrawable.java
new file mode 100644
index 0000000..3e40443
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/FixedSizeDrawable.java
@@ -0,0 +1,66 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.graphics.drawable.Drawable;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.util.Slog;
+
+class FixedSizeDrawable extends Drawable {
+    Drawable mDrawable;
+    int mLeft;
+    int mTop;
+    int mRight;
+    int mBottom;
+
+    FixedSizeDrawable(Drawable that) {
+        mDrawable = that;
+    }
+
+    public void setFixedBounds(int l, int t, int r, int b) {
+        mLeft = l;
+        mTop = t;
+        mRight = r;
+        mBottom = b;
+    }
+
+    public void setBounds(Rect bounds) {
+        mDrawable.setBounds(mLeft, mTop, mRight, mBottom);
+    }
+
+    public void setBounds(int l, int t, int r, int b) {
+        mDrawable.setBounds(mLeft, mTop, mRight, mBottom);
+    }
+
+    public void draw(Canvas canvas) {
+        mDrawable.draw(canvas);
+    }
+
+    public int getOpacity() {
+        return mDrawable.getOpacity();
+    }
+
+    public void setAlpha(int alpha) {
+        mDrawable.setAlpha(alpha);
+    }
+
+    public void setColorFilter(ColorFilter cf) {
+        mDrawable.setColorFilter(cf);
+    }
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconData.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconData.java
new file mode 100644
index 0000000..354cf1c
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconData.java
@@ -0,0 +1,122 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.util.Slog;
+
+public class IconData {
+    /**
+     * Indicates ths item represents a piece of text.
+     */
+    public static final int TEXT = 1;
+    
+    /**
+     * Indicates ths item represents an icon.
+     */
+    public static final int ICON = 2;
+
+    /**
+     * The type of this item. One of TEXT, ICON, or LEVEL_ICON.
+     */
+    public int type;
+
+    /**
+     * The slot that this icon will be in if it is not a notification
+     */
+    public String slot;
+
+    /**
+     * The package containting the icon to draw for this item. Valid if this is
+     * an ICON type.
+     */
+    public String iconPackage;
+    
+    /**
+     * The icon to draw for this item. Valid if this is an ICON type.
+     */
+    public int iconId;
+    
+    /**
+     * The level associated with the icon. Valid if this is a LEVEL_ICON type.
+     */
+    public int iconLevel;
+    
+    /**
+     * The "count" number.
+     */
+    public int number;
+
+    /**
+     * The text associated with the icon. Valid if this is a TEXT type.
+     */
+    public CharSequence text;
+
+    private IconData() {
+    }
+
+    public static IconData makeIcon(String slot,
+            String iconPackage, int iconId, int iconLevel, int number) {
+        IconData data = new IconData();
+        data.type = ICON;
+        data.slot = slot;
+        data.iconPackage = iconPackage;
+        data.iconId = iconId;
+        data.iconLevel = iconLevel;
+        data.number = number;
+        return data;
+    }
+    
+    public static IconData makeText(String slot, CharSequence text) {
+        IconData data = new IconData();
+        data.type = TEXT;
+        data.slot = slot;
+        data.text = text;
+        return data;
+    }
+
+    public void copyFrom(IconData that) {
+        this.type = that.type;
+        this.slot = that.slot;
+        this.iconPackage = that.iconPackage;
+        this.iconId = that.iconId;
+        this.iconLevel = that.iconLevel;
+        this.number = that.number;
+        this.text = that.text; // should we clone this?
+    }
+
+    public IconData clone() {
+        IconData that = new IconData();
+        that.copyFrom(this);
+        return that;
+    }
+
+    public String toString() {
+        if (this.type == TEXT) {
+            return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null")
+                    + " text='" + this.text + "')"; 
+        }
+        else if (this.type == ICON) {
+            return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null")
+                    + " package=" + this.iconPackage
+                    + " iconId=" + Integer.toHexString(this.iconId)
+                    + " iconLevel=" + this.iconLevel + ")"; 
+        }
+        else {
+            return "IconData(type=" + type + ")";
+        }
+    }
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconMerger.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconMerger.java
new file mode 100644
index 0000000..8fcd36f
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconMerger.java
@@ -0,0 +1,132 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+public class IconMerger extends LinearLayout {
+    PhoneStatusBarService service;
+    StatusBarIcon moreIcon;
+
+    public IconMerger(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+
+        final int maxWidth = r - l;
+        final int N = getChildCount();
+        int i;
+
+        // get the rightmost one, and see if we even need to do anything
+        int fitRight = -1;
+        for (i=N-1; i>=0; i--) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                fitRight = child.getRight();
+                break;
+            }
+        }
+
+        // find the first visible one that isn't the more icon
+        View moreView = null;
+        int fitLeft = -1;
+        int startIndex = -1;
+        for (i=0; i<N; i++) {
+            final View child = getChildAt(i);
+            if (com.android.internal.R.drawable.stat_notify_more == child.getId()) {
+                moreView = child;
+                startIndex = i+1;
+            }
+            else if (child.getVisibility() != GONE) {
+                fitLeft = child.getLeft();
+                break;
+            }
+        }
+
+        if (moreView == null || startIndex < 0) {
+            throw new RuntimeException("Status Bar / IconMerger moreView == null");
+        }
+        
+        // if it fits without the more icon, then hide the more icon and update fitLeft
+        // so everything gets pushed left
+        int adjust = 0;
+        if (fitRight - fitLeft <= maxWidth) {
+            adjust = fitLeft - moreView.getLeft();
+            fitLeft -= adjust;
+            fitRight -= adjust;
+            moreView.layout(0, moreView.getTop(), 0, moreView.getBottom());
+        }
+        int extra = fitRight - r;
+        int shift = -1;
+
+        int breakingPoint = fitLeft + extra + adjust;
+        int number = 0;
+        for (i=startIndex; i<N; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                int childLeft = child.getLeft();
+                int childRight = child.getRight();
+                if (childLeft < breakingPoint) {
+                    // hide this one
+                    child.layout(0, child.getTop(), 0, child.getBottom());
+                    int n = this.service.getIconNumberForView(child);
+                    if (n == 0) {
+                        number += 1;
+                    } else if (n > 0) {
+                        number += n;
+                    }
+                } else {
+                    // decide how much to shift by
+                    if (shift < 0) {
+                        shift = childLeft - fitLeft;
+                    }
+                    // shift this left by shift
+                    child.layout(childLeft-shift, child.getTop(),
+                                    childRight-shift, child.getBottom());
+                }
+            }
+        }
+        
+        // BUG: Updating the text during the layout here doesn't seem to cause
+        // the view to be redrawn fully.  The text view gets resized correctly, but the
+        // text contents aren't drawn properly.  To work around this, we post a message
+        // and provide the value later.  We're the only one changing this value show it
+        // should be ordered correctly.
+        if (false) {
+            this.moreIcon.update(number);
+        } else {
+            mBugWorkaroundNumber = number;
+            mBugWorkaroundHandler.post(mBugWorkaroundRunnable);
+        }
+    }
+
+    private int mBugWorkaroundNumber;
+    private Handler mBugWorkaroundHandler = new Handler();
+    private Runnable mBugWorkaroundRunnable = new Runnable() {
+        public void run() {
+            IconMerger.this.moreIcon.update(mBugWorkaroundNumber);
+            IconMerger.this.moreIcon.view.invalidate();
+        }
+    };
+}
diff --git a/services/java/com/android/server/status/TickerView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/LatestItemView.java
similarity index 66%
copy from services/java/com/android/server/status/TickerView.java
copy to packages/StatusBarPhone/src/com/android/policy/statusbar/phone/LatestItemView.java
index 099dffb..36e1bfb 100644
--- a/services/java/com/android/server/status/TickerView.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/LatestItemView.java
@@ -14,25 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.server.status;
+package com.android.policy.statusbar.phone;
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.widget.TextSwitcher;
+import android.util.Slog;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
 
+public class LatestItemView extends FrameLayout {
 
-public class TickerView extends TextSwitcher
-{
-    Ticker mTicker;
-
-    public TickerView(Context context, AttributeSet attrs) {
+    public LatestItemView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        mTicker.reflowText();
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        return onTouchEvent(ev);
     }
 }
-
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationData.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationData.java
new file mode 100644
index 0000000..ca2d79b
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationData.java
@@ -0,0 +1,44 @@
+/*
+ * 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.policy.statusbar.phone;
+
+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/TickerView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationLinearLayout.java
similarity index 65%
copy from services/java/com/android/server/status/TickerView.java
copy to packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationLinearLayout.java
index 099dffb..a5d0c6a 100644
--- a/services/java/com/android/server/status/TickerView.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationLinearLayout.java
@@ -14,25 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.server.status;
+package com.android.policy.statusbar.phone;
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.widget.TextSwitcher;
+import android.widget.LinearLayout;
 
 
-public class TickerView extends TextSwitcher
-{
-    Ticker mTicker;
-
-    public TickerView(Context context, AttributeSet attrs) {
+public class NotificationLinearLayout extends LinearLayout {
+    public NotificationLinearLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        mTicker.reflowText();
-    }
 }
 
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationViewList.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationViewList.java
new file mode 100644
index 0000000..300d58b
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationViewList.java
@@ -0,0 +1,276 @@
+/*
+ * 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.policy.statusbar.phone;
+
+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(PhoneStatusBarService.TAG, "Couldn't find notification in NotificationViewList.");
+        Slog.e(PhoneStatusBarService.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 (PhoneStatusBarService.SPEW) {
+            Slog.d(PhoneStatusBarService.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 (PhoneStatusBarService.SPEW) {
+            Slog.d(PhoneStatusBarService.TAG, "after add NotificationViewList index=" + index);
+            dump(notification);
+        }
+    }
+
+    void dump(StatusBarNotification notification) {
+        if (PhoneStatusBarService.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(PhoneStatusBarService.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(PhoneStatusBarService.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/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java
index b9b38db..daed2ef 100644
--- a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java
@@ -16,25 +16,306 @@
 
 package com.android.policy.statusbar.phone;
 
-import android.app.Service;
+import com.android.internal.util.CharSequences;
+
+import android.app.ActivityManagerNative;
+import android.app.Dialog;
 import android.app.IStatusBar;
 import android.app.IStatusBarService;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
 import android.os.SystemClock;
-import android.util.Log;
+import android.provider.Telephony;
+import android.util.Slog;
+import android.view.Display;
 import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.FrameLayout;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Set;
+
 
 public class PhoneStatusBarService extends StatusBarService {
+    static final String TAG = "StatusBar";
+    static final boolean SPEW = false;
+
+    public static final String ACTION_STATUSBAR_START
+            = "com.android.internal.policy.statusbar.START";
+
+    static final int EXPANDED_LEAVE_ALONE = -10000;
+    static final int EXPANDED_FULL_OPEN = -10001;
+
+    private static final int MSG_ANIMATE = 1000;
+    private static final int MSG_ANIMATE_REVEAL = 1001;
+
+    private static final int OP_ADD_ICON = 1;
+    private static final int OP_UPDATE_ICON = 2;
+    private static final int OP_REMOVE_ICON = 3;
+    private static final int OP_SET_VISIBLE = 4;
+    private static final int OP_EXPAND = 5;
+    private static final int OP_TOGGLE = 6;
+    private static final int OP_DISABLE = 7;
+    private class PendingOp {
+        IBinder key;
+        int code;
+        IconData iconData;
+        NotificationData notificationData;
+        boolean visible;
+        int integer;
+    }
+
+    private class DisableRecord implements IBinder.DeathRecipient {
+        String pkg;
+        int what;
+        IBinder token;
+
+        public void binderDied() {
+            Slog.i(TAG, "binder died for pkg=" + pkg);
+            disable(0, token, pkg);
+            token.unlinkToDeath(this, 0);
+        }
+    }
+
+    public interface NotificationCallbacks {
+        void onSetDisabled(int status);
+        void onClearAll();
+        void onNotificationClick(String pkg, String tag, int id);
+        void onPanelRevealed();
+    }
+
+    private class ExpandedDialog extends Dialog {
+        ExpandedDialog(Context context) {
+            super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
+        }
+
+        @Override
+        public boolean dispatchKeyEvent(KeyEvent event) {
+            boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+            switch (event.getKeyCode()) {
+            case KeyEvent.KEYCODE_BACK:
+                if (!down) {
+                    PhoneStatusBarService.this.deactivate();
+                }
+                return true;
+            }
+            return super.dispatchKeyEvent(event);
+        }
+    }
+    
+    final Display mDisplay;
+    StatusBarView mStatusBarView;
+    int mPixelFormat;
+    H mHandler = new H();
+    Object mQueueLock = new Object();
+    ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>();
+    NotificationCallbacks mNotificationCallbacks;
+    
+    // All accesses to mIconMap and mNotificationData are syncronized on those objects,
+    // but this is only so dump() can work correctly.  Modifying these outside of the UI
+    // thread will not work, there are places in the code that unlock and reaquire between
+    // reads and require them to not be modified.
+
+    // icons
+    HashMap<IBinder,StatusBarIcon> mIconMap = new HashMap<IBinder,StatusBarIcon>();
+    ArrayList<StatusBarIcon> mIconList = new ArrayList<StatusBarIcon>();
+    String[] mRightIconSlots;
+    StatusBarIcon[] mRightIcons;
+    LinearLayout mIcons;
+    IconMerger mNotificationIcons;
+    LinearLayout mStatusIcons;
+    StatusBarIcon mMoreIcon;
+    private UninstallReceiver mUninstallReceiver;
+
+    // expanded notifications
+    NotificationViewList mNotificationData = new NotificationViewList();
+    Dialog mExpandedDialog;
+    ExpandedView mExpandedView;
+    WindowManager.LayoutParams mExpandedParams;
+    ScrollView mScrollView;
+    View mNotificationLinearLayout;
+    TextView mOngoingTitle;
+    LinearLayout mOngoingItems;
+    TextView mLatestTitle;
+    LinearLayout mLatestItems;
+    TextView mNoNotificationsTitle;
+    TextView mSpnLabel;
+    TextView mPlmnLabel;
+    TextView mClearButton;
+    View mExpandedContents;
+    CloseDragHandle mCloseView;
+    int[] mPositionTmp = new int[2];
+    boolean mExpanded;
+    boolean mExpandedVisible;
+
+    // the date view
+    DateView mDateView;
+
+    // the tracker view
+    TrackingView mTrackingView;
+    WindowManager.LayoutParams mTrackingParams;
+    int mTrackingPosition; // the position of the top of the tracking view.
+
+    // ticker
+    private Ticker mTicker;
+    private View mTickerView;
+    private boolean mTicking;
+    
+    // Tracking finger for opening/closing.
+    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
+    boolean mTracking;
+    VelocityTracker mVelocityTracker;
+    
+    static final int ANIM_FRAME_DURATION = (1000/60);
+    
+    boolean mAnimating;
+    long mCurAnimationTime;
+    float mDisplayHeight;
+    float mAnimY;
+    float mAnimVel;
+    float mAnimAccel;
+    long mAnimLastTime;
+    boolean mAnimatingReveal = false;
+    int mViewDelta;
+    int[] mAbsPos = new int[2];
+    
+    // for disabling the status bar
+    ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
+    int mDisabled = 0;
+
+    /**
+     * Construct the service, add the status bar view to the window manager
+     */
+    public PhoneStatusBarService(Context context) {
+        mDisplay = ((WindowManager)context.getSystemService(
+                Context.WINDOW_SERVICE)).getDefaultDisplay();
+        makeStatusBarView(context);
+        mUninstallReceiver = new UninstallReceiver();
+    }
+
+    public void setNotificationCallbacks(NotificationCallbacks listener) {
+        mNotificationCallbacks = listener;
+    }
+
+    // ================================================================================
+    // Constructing the view
+    // ================================================================================
+    private void makeStatusBarView(Context context) {
+        Resources res = context.getResources();
+        mRightIconSlots = res.getStringArray(R.array.status_bar_icon_order);
+        mRightIcons = new StatusBarIcon[mRightIconSlots.length];
+
+        ExpandedView expanded = (ExpandedView)View.inflate(context,
+                com.android.internal.R.layout.status_bar_expanded, null);
+        expanded.mService = this;
+        StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
+        sb.mService = this;
+
+        // figure out which pixel-format to use for the status bar.
+        mPixelFormat = PixelFormat.TRANSLUCENT;
+        Drawable bg = sb.getBackground();
+        if (bg != null) {
+            mPixelFormat = bg.getOpacity();
+        }
+
+        mStatusBarView = sb;
+        mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
+        mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
+        mNotificationIcons.service = this;
+        mIcons = (LinearLayout)sb.findViewById(R.id.icons);
+        mTickerView = sb.findViewById(R.id.ticker);
+        mDateView = (DateView)sb.findViewById(R.id.date);
+
+        mExpandedDialog = new ExpandedDialog(context);
+        mExpandedView = expanded;
+        mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
+        mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
+        mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
+        mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
+        mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
+        mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
+        mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
+        mClearButton.setOnClickListener(mClearButtonListener);
+        mSpnLabel = (TextView)expanded.findViewById(R.id.spnLabel);
+        mPlmnLabel = (TextView)expanded.findViewById(R.id.plmnLabel);
+        mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
+        mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
+
+        mOngoingTitle.setVisibility(View.GONE);
+        mLatestTitle.setVisibility(View.GONE);
+        
+        mTicker = new MyTicker(context, sb);
+
+        TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
+        tickerView.mTicker = mTicker;
+
+        mTrackingView = (TrackingView)View.inflate(context,
+                com.android.internal.R.layout.status_bar_tracking, null);
+        mTrackingView.mService = this;
+        mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
+        mCloseView.mService = this;
+
+        mEdgeBorder = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_edge_ignore);
+
+        // add the more icon for the notifications
+        IconData moreData = IconData.makeIcon(null, context.getPackageName(),
+                R.drawable.stat_notify_more, 0, 42);
+        mMoreIcon = new StatusBarIcon(context, moreData, mNotificationIcons);
+        mMoreIcon.view.setId(R.drawable.stat_notify_more);
+        mNotificationIcons.moreIcon = mMoreIcon;
+        mNotificationIcons.addView(mMoreIcon.view);
+
+        // set the inital view visibility
+        setAreThereNotifications();
+        mDateView.setVisibility(View.INVISIBLE);
+
+        // before we register for broadcasts
+        mPlmnLabel.setText(com.android.internal.R.string.lockscreen_carrier_default);
+        mPlmnLabel.setVisibility(View.VISIBLE);
+        mSpnLabel.setText("");
+        mSpnLabel.setVisibility(View.GONE);
+
+        // receive broadcasts
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
+        context.registerReceiver(mBroadcastReceiver, filter);
+    }
 
     @Override
     protected void addStatusBarView() {
@@ -55,4 +336,1538 @@
 
         WindowManagerImpl.getDefault().addView(view, lp);
     }
+
+    // ================================================================================
+    // From IStatusBarService
+    // ================================================================================
+    public void activate() {
+        enforceExpandStatusBar();
+        addPendingOp(OP_EXPAND, null, true);
+    }
+
+    public void deactivate() {
+        enforceExpandStatusBar();
+        addPendingOp(OP_EXPAND, null, false);
+    }
+
+    public void toggle() {
+        enforceExpandStatusBar();
+        addPendingOp(OP_TOGGLE, null, false);
+    }
+
+    public void disable(int what, IBinder token, String pkg) {
+        enforceStatusBar();
+        synchronized (mNotificationCallbacks) {
+            // This is a little gross, but I think it's safe as long as nobody else
+            // synchronizes on mNotificationCallbacks.  It's important that the the callback
+            // and the pending op get done in the correct order and not interleaved with
+            // other calls, otherwise they'll get out of sync.
+            int net;
+            synchronized (mDisableRecords) {
+                manageDisableListLocked(what, token, pkg);
+                net = gatherDisableActionsLocked();
+                mNotificationCallbacks.onSetDisabled(net);
+            }
+            addPendingOp(OP_DISABLE, net);
+        }
+    }
+
+    public IBinder addIcon(String slot, String iconPackage, int iconId, int iconLevel) {
+        enforceStatusBar();
+        return addIcon(IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
+    }
+
+    public void updateIcon(IBinder key,
+            String slot, String iconPackage, int iconId, int iconLevel) {
+        enforceStatusBar();
+        updateIcon(key, IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
+    }
+
+    public void removeIcon(IBinder key) {
+        enforceStatusBar();
+        addPendingOp(OP_REMOVE_ICON, key, null, null, -1);
+    }
+
+    private void enforceStatusBar() {
+        enforceCallingOrSelfPermission(
+                android.Manifest.permission.STATUS_BAR,
+                "PhoneStatusBarService");
+    }
+
+    private void enforceExpandStatusBar() {
+        enforceCallingOrSelfPermission(
+                android.Manifest.permission.EXPAND_STATUS_BAR,
+                "PhoneStatusBarService");
+    }
+
+    public void registerStatusBar(IStatusBar bar) {
+        Slog.d(TAG, "registerStatusBar bar=" + bar);
+    }
+    
+
+    // ================================================================================
+    // Can be called from any thread
+    // ================================================================================
+    public IBinder addIcon(IconData data, NotificationData n) {
+        // TODO: Call onto the IStatusBar
+        int slot;
+        // assert early-on if they using a slot that doesn't exist.
+        if (data != null && n == null) {
+            slot = getRightIconIndex(data.slot);
+            if (slot < 0) {
+                throw new SecurityException("invalid status bar icon slot: "
+                        + (data.slot != null ? "'" + data.slot + "'" : "null"));
+            }
+        } else {
+            slot = -1;
+        }
+        IBinder key = new Binder();
+        addPendingOp(OP_ADD_ICON, key, data, n, -1);
+        return key;
+    }
+
+    public void updateIcon(IBinder key, IconData data, NotificationData n) {
+        addPendingOp(OP_UPDATE_ICON, key, data, n, -1);
+    }
+
+    public void setIconVisibility(IBinder key, boolean visible) {
+        addPendingOp(OP_SET_VISIBLE, key, visible);
+    }
+
+    private void addPendingOp(int code, IBinder key, IconData data, NotificationData n, int i) {
+        synchronized (mQueueLock) {
+            PendingOp op = new PendingOp();
+            op.key = key;
+            op.code = code;
+            op.iconData = data == null ? null : data.clone();
+            op.notificationData = n;
+            op.integer = i;
+            mQueue.add(op);
+            if (mQueue.size() == 1) {
+                mHandler.sendEmptyMessage(2);
+            }
+        }
+    }
+
+    private void addPendingOp(int code, IBinder key, boolean visible) {
+        synchronized (mQueueLock) {
+            PendingOp op = new PendingOp();
+            op.key = key;
+            op.code = code;
+            op.visible = visible;
+            mQueue.add(op);
+            if (mQueue.size() == 1) {
+                mHandler.sendEmptyMessage(1);
+            }
+        }
+    }
+
+    private void addPendingOp(int code, int integer) {
+        synchronized (mQueueLock) {
+            PendingOp op = new PendingOp();
+            op.code = code;
+            op.integer = integer;
+            mQueue.add(op);
+            if (mQueue.size() == 1) {
+                mHandler.sendEmptyMessage(1);
+            }
+        }
+    }
+
+    // lock on mDisableRecords
+    void manageDisableListLocked(int what, IBinder token, String pkg) {
+        if (SPEW) {
+            Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what)
+                    + " pkg=" + pkg);
+        }
+        // update the list
+        synchronized (mDisableRecords) {
+            final int N = mDisableRecords.size();
+            DisableRecord tok = null;
+            int i;
+            for (i=0; i<N; i++) {
+                DisableRecord t = mDisableRecords.get(i);
+                if (t.token == token) {
+                    tok = t;
+                    break;
+                }
+            }
+            if (what == 0 || !token.isBinderAlive()) {
+                if (tok != null) {
+                    mDisableRecords.remove(i);
+                    tok.token.unlinkToDeath(tok, 0);
+                }
+            } else {
+                if (tok == null) {
+                    tok = new DisableRecord();
+                    try {
+                        token.linkToDeath(tok, 0);
+                    }
+                    catch (RemoteException ex) {
+                        return; // give up
+                    }
+                    mDisableRecords.add(tok);
+                }
+                tok.what = what;
+                tok.token = token;
+                tok.pkg = pkg;
+            }
+        }
+    }
+
+    // lock on mDisableRecords
+    int gatherDisableActionsLocked() {
+        final int N = mDisableRecords.size();
+        // gather the new net flags
+        int net = 0;
+        for (int i=0; i<N; i++) {
+            net |= mDisableRecords.get(i).what;
+        }
+        return net;
+    }
+
+    private int getRightIconIndex(String slot) {
+        final int N = mRightIconSlots.length;
+        for (int i=0; i<N; i++) {
+            if (mRightIconSlots[i].equals(slot)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    // ================================================================================
+    // Always called from UI thread
+    // ================================================================================
+    /**
+     * All changes to the status bar and notifications funnel through here and are batched.
+     */
+    private class H extends Handler {
+        public void handleMessage(Message m) {
+            if (m.what == MSG_ANIMATE) {
+                doAnimation();
+                return;
+            }
+            if (m.what == MSG_ANIMATE_REVEAL) {
+                doRevealAnimation();
+                return;
+            }
+
+            ArrayList<PendingOp> queue;
+            synchronized (mQueueLock) {
+                queue = mQueue;
+                mQueue = new ArrayList<PendingOp>();
+            }
+
+            boolean wasExpanded = mExpanded;
+
+            // for each one in the queue, find all of the ones with the same key
+            // and collapse that down into a final op and/or call to setVisibility, etc
+            boolean expand = wasExpanded;
+            boolean doExpand = false;
+            boolean doDisable = false;
+            int disableWhat = 0;
+            int N = queue.size();
+            while (N > 0) {
+                PendingOp op = queue.get(0);
+                boolean doOp = false;
+                boolean visible = false;
+                boolean doVisibility = false;
+                if (op.code == OP_SET_VISIBLE) {
+                    doVisibility = true;
+                    visible = op.visible;
+                }
+                else if (op.code == OP_EXPAND) {
+                    doExpand = true;
+                    expand = op.visible;
+                }
+                else if (op.code == OP_TOGGLE) {
+                    doExpand = true;
+                    expand = !expand;
+                }
+                else {
+                    doOp = true;
+                }
+
+                if (alwaysHandle(op.code)) {
+                    // coalesce these
+                    for (int i=1; i<N; i++) {
+                        PendingOp o = queue.get(i);
+                        if (!alwaysHandle(o.code) && o.key == op.key) {
+                            if (o.code == OP_SET_VISIBLE) {
+                                visible = o.visible;
+                                doVisibility = true;
+                            }
+                            else if (o.code == OP_EXPAND) {
+                                expand = o.visible;
+                                doExpand = true;
+                            }
+                            else {
+                                op.code = o.code;
+                                op.iconData = o.iconData;
+                                op.notificationData = o.notificationData;
+                            }
+                            queue.remove(i);
+                            i--;
+                            N--;
+                        }
+                    }
+                }
+
+                queue.remove(0);
+                N--;
+
+                if (doOp) {
+                    switch (op.code) {
+                        case OP_ADD_ICON:
+                        case OP_UPDATE_ICON:
+                            performAddUpdateIcon(op.key, op.iconData, op.notificationData);
+                            break;
+                        case OP_REMOVE_ICON:
+                            performRemoveIcon(op.key);
+                            break;
+                        case OP_DISABLE:
+                            doDisable = true;
+                            disableWhat = op.integer;
+                            break;
+                    }
+                }
+                if (doVisibility && op.code != OP_REMOVE_ICON) {
+                    performSetIconVisibility(op.key, visible);
+                }
+            }
+
+            if (queue.size() != 0) {
+                throw new RuntimeException("Assertion failed: queue.size=" + queue.size());
+            }
+            if (doExpand) {
+                // this is last so that we capture all of the pending changes before doing it
+                if (expand) {
+                    animateExpand();
+                } else {
+                    animateCollapse();
+                }
+            }
+            if (doDisable) {
+                performDisableActions(disableWhat);
+            }
+        }
+    }
+
+    private boolean alwaysHandle(int code) {
+        return code == OP_DISABLE;
+    }
+
+    /* private */ void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
+                        throws StatusBarException {
+        if (SPEW) {
+            Slog.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key);
+        }
+        // notification
+        if (n != null) {
+            StatusBarNotification notification = getNotification(key);
+            NotificationData oldData = null;
+            if (notification == null) {
+                // add
+                notification = new StatusBarNotification();
+                notification.key = key;
+                notification.data = n;
+                synchronized (mNotificationData) {
+                    mNotificationData.add(notification);
+                }
+                addNotificationView(notification);
+                setAreThereNotifications();
+            } else {
+                // update
+                oldData = notification.data;
+                notification.data = n;
+                updateNotificationView(notification, oldData);
+            }
+            // Show the ticker if one is requested, and the text is different
+            // than the currently displayed ticker.  Also don't do this
+            // until status bar window is attached to the window manager,
+            // because...  well, what's the point otherwise?  And trying to
+            // run a ticker without being attached will crash!
+            if (n.tickerText != null && mStatusBarView.getWindowToken() != null
+                    && (oldData == null
+                        || oldData.tickerText == null
+                        || !CharSequences.equals(oldData.tickerText, n.tickerText))) {
+                if (0 == (mDisabled & 
+                    (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+                    mTicker.addEntry(n, StatusBarIcon.getIcon(this, data), n.tickerText);
+                }
+            }
+            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+        }
+
+        // icon
+        synchronized (mIconMap) {
+            StatusBarIcon icon = mIconMap.get(key);
+            if (icon == null) {
+                // add
+                LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;
+
+                icon = new StatusBarIcon(this, data, v);
+                mIconMap.put(key, icon);
+                mIconList.add(icon);
+
+                if (n == null) {
+                    int slotIndex = getRightIconIndex(data.slot);
+                    StatusBarIcon[] rightIcons = mRightIcons;
+                    if (rightIcons[slotIndex] == null) {
+                        int pos = 0;
+                        for (int i=mRightIcons.length-1; i>slotIndex; i--) {
+                            StatusBarIcon ic = rightIcons[i];
+                            if (ic != null) {
+                                pos++;
+                            }
+                        }
+                        rightIcons[slotIndex] = icon;
+                        mStatusIcons.addView(icon.view, pos);
+                    } else {
+                        Slog.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot);
+                        mIconMap.remove(key);
+                        mIconList.remove(icon);
+                        return ;
+                    }
+                } else {
+                    int iconIndex = mNotificationData.getIconIndex(n);
+                    mNotificationIcons.addView(icon.view, iconIndex);
+                }
+            } else {
+                if (n == null) {
+                    // right hand side icons -- these don't reorder
+                    icon.update(this, data);
+                } else {
+                    // remove old
+                    ViewGroup parent = (ViewGroup)icon.view.getParent();
+                    parent.removeView(icon.view);
+                    // add new
+                    icon.update(this, data);
+                    int iconIndex = mNotificationData.getIconIndex(n);
+                    mNotificationIcons.addView(icon.view, iconIndex);
+                }
+            }
+        }
+    }
+
+    /* private */ void performSetIconVisibility(IBinder key, boolean visible) {
+        synchronized (mIconMap) {
+            if (SPEW) {
+                Slog.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible);
+            }
+            StatusBarIcon icon = mIconMap.get(key);
+            icon.view.setVisibility(visible ? View.VISIBLE : View.GONE);
+        }
+    }
+    
+    /* private */ void performRemoveIcon(IBinder key) {
+        synchronized (this) {
+            if (SPEW) {
+                Slog.d(TAG, "performRemoveIcon key=" + key);
+            }
+            StatusBarIcon icon = mIconMap.remove(key);
+            mIconList.remove(icon);
+            if (icon != null) {
+                ViewGroup parent = (ViewGroup)icon.view.getParent();
+                parent.removeView(icon.view);
+                int slotIndex = getRightIconIndex(icon.mData.slot);
+                if (slotIndex >= 0) {
+                    mRightIcons[slotIndex] = null;
+                }
+            }
+            StatusBarNotification notification = getNotification(key);
+            if (notification != null) {
+                removeNotificationView(notification);
+                synchronized (mNotificationData) {
+                    mNotificationData.remove(notification);
+                }
+                setAreThereNotifications();
+            }
+        }
+    }
+
+    int getIconNumberForView(View v) {
+        synchronized (mIconMap) {
+            StatusBarIcon icon = null;
+            final int N = mIconList.size();
+            for (int i=0; i<N; i++) {
+                StatusBarIcon ic = mIconList.get(i);
+                if (ic.view == v) {
+                    icon = ic;
+                    break;
+                }
+            }
+            if (icon != null) {
+                return icon.getNumber();
+            } else {
+                return -1;
+            }
+        }
+    }
+
+
+    StatusBarNotification getNotification(IBinder key) {
+        synchronized (mNotificationData) {
+            return mNotificationData.get(key);
+        }
+    }
+
+    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
+        public void onFocusChange(View v, boolean hasFocus) {
+            // Because 'v' is a ViewGroup, all its children will be (un)selected
+            // too, which allows marqueeing to work.
+            v.setSelected(hasFocus);
+        }
+    };
+    
+    View makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
+        NotificationData n = notification.data;
+        RemoteViews remoteViews = n.contentView;
+        if (remoteViews == null) {
+            return null;
+        }
+
+        // create the row view
+        LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        View row = inflater.inflate(com.android.internal.R.layout.status_bar_latest_event, parent, false);
+
+        // bind the click event to the content area
+        ViewGroup content = (ViewGroup)row.findViewById(com.android.internal.R.id.content);
+        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+        content.setOnFocusChangeListener(mFocusChangeListener);
+        PendingIntent contentIntent = n.contentIntent;
+        if (contentIntent != null) {
+            content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
+        }
+
+        View child = null;
+        Exception exception = null;
+        try {
+            child = remoteViews.apply(this, content);
+        }
+        catch (RuntimeException e) {
+            exception = e;
+        }
+        if (child == null) {
+            Slog.e(TAG, "couldn't inflate view for package " + n.pkg, exception);
+            return null;
+        }
+        content.addView(child);
+
+        row.setDrawingCacheEnabled(true);
+
+        notification.view = row;
+        notification.contentView = child;
+
+        return row;
+    }
+
+    void addNotificationView(StatusBarNotification notification) {
+        if (notification.view != null) {
+            throw new RuntimeException("Assertion failed: notification.view="
+                    + notification.view);
+        }
+
+        LinearLayout parent = notification.data.ongoingEvent ? mOngoingItems : mLatestItems;
+
+        View child = makeNotificationView(notification, parent);
+        if (child == null) {
+            return ;
+        }
+
+        int index = mNotificationData.getExpandedIndex(notification);
+        parent.addView(child, index);
+    }
+
+    /**
+     * Remove the old one and put the new one in its place.
+     * @param notification the notification
+     */
+    void updateNotificationView(StatusBarNotification notification, NotificationData oldData) {
+        NotificationData n = notification.data;
+        if (oldData != null && n != null
+                && n.when == oldData.when
+                && n.ongoingEvent == oldData.ongoingEvent
+                && n.contentView != null && oldData.contentView != null
+                && n.contentView.getPackage() != null
+                && oldData.contentView.getPackage() != null
+                && oldData.contentView.getPackage().equals(n.contentView.getPackage())
+                && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()
+                && notification.view != null) {
+            mNotificationData.update(notification);
+            try {
+                n.contentView.reapply(this, notification.contentView);
+
+                // update the contentIntent
+                ViewGroup content = (ViewGroup)notification.view.findViewById(
+                        com.android.internal.R.id.content);
+                PendingIntent contentIntent = n.contentIntent;
+                if (contentIntent != null) {
+                    content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
+                }
+            }
+            catch (RuntimeException e) {
+                // It failed to add cleanly.  Log, and remove the view from the panel.
+                Slog.w(TAG, "couldn't reapply views for package " + n.contentView.getPackage(), e);
+                removeNotificationView(notification);
+            }
+        } else {
+            mNotificationData.update(notification);
+            removeNotificationView(notification);
+            addNotificationView(notification);
+        }
+        setAreThereNotifications();
+    }
+
+    void removeNotificationView(StatusBarNotification notification) {
+        View v = notification.view;
+        if (v != null) {
+            ViewGroup parent = (ViewGroup)v.getParent();
+            parent.removeView(v);
+            notification.view = null;
+        }
+    }
+
+    private void setAreThereNotifications() {
+        boolean ongoing = mOngoingItems.getChildCount() != 0;
+        boolean latest = mLatestItems.getChildCount() != 0;
+
+        if (mNotificationData.hasClearableItems()) {
+            mClearButton.setVisibility(View.VISIBLE);
+        } else {
+            mClearButton.setVisibility(View.INVISIBLE);
+        }
+
+        mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
+        mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
+
+        if (ongoing || latest) {
+            mNoNotificationsTitle.setVisibility(View.GONE);
+        } else {
+            mNoNotificationsTitle.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private void makeExpandedVisible() {
+        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
+        if (mExpandedVisible) {
+            return;
+        }
+        mExpandedVisible = true;
+        panelSlightlyVisible(true);
+        
+        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        mExpandedView.requestFocus(View.FOCUS_FORWARD);
+        mTrackingView.setVisibility(View.VISIBLE);
+        
+        if (!mTicking) {
+            setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+        }
+    }
+    
+    void animateExpand() {
+        if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+            return ;
+        }
+        if (mExpanded) {
+            return;
+        }
+
+        prepareTracking(0, true);
+        performFling(0, 2000.0f, true);
+    }
+    
+    void animateCollapse() {
+        if (SPEW) {
+            Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
+                    + " mExpandedVisible=" + mExpandedVisible
+                    + " mExpanded=" + mExpanded
+                    + " mAnimating=" + mAnimating
+                    + " mAnimY=" + mAnimY
+                    + " mAnimVel=" + mAnimVel);
+        }
+        
+        if (!mExpandedVisible) {
+            return;
+        }
+
+        int y;
+        if (mAnimating) {
+            y = (int)mAnimY;
+        } else {
+            y = mDisplay.getHeight()-1;
+        }
+        // Let the fling think that we're open so it goes in the right direction
+        // and doesn't try to re-open the windowshade.
+        mExpanded = true;
+        prepareTracking(y, false);
+        performFling(y, -2000.0f, true);
+    }
+    
+    void performExpand() {
+        if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+            return ;
+        }
+        if (mExpanded) {
+            return;
+        }
+
+        // It seems strange to sometimes not expand...
+        if (false) {
+            synchronized (mNotificationData) {
+                if (mNotificationData.size() == 0) {
+                    return;
+                }
+            }
+        }
+        
+        mExpanded = true;
+        makeExpandedVisible();
+        updateExpandedViewPos(EXPANDED_FULL_OPEN);
+
+        if (false) postStartTracing();
+    }
+
+    void performCollapse() {
+        if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
+                + " mExpandedVisible=" + mExpandedVisible);
+        
+        if (!mExpandedVisible) {
+            return;
+        }
+        mExpandedVisible = false;
+        panelSlightlyVisible(false);
+        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        mTrackingView.setVisibility(View.GONE);
+
+        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
+            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+        }
+        setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
+        
+        if (!mExpanded) {
+            return;
+        }
+        mExpanded = false;
+    }
+
+    void doAnimation() {
+        if (mAnimating) {
+            if (SPEW) Slog.d(TAG, "doAnimation");
+            if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
+            incrementAnim();
+            if (SPEW) Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
+            if (mAnimY >= mDisplay.getHeight()-1) {
+                if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
+                mAnimating = false;
+                updateExpandedViewPos(EXPANDED_FULL_OPEN);
+                performExpand();
+            }
+            else if (mAnimY < mStatusBarView.getHeight()) {
+                if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
+                mAnimating = false;
+                updateExpandedViewPos(0);
+                performCollapse();
+            }
+            else {
+                updateExpandedViewPos((int)mAnimY);
+                mCurAnimationTime += ANIM_FRAME_DURATION;
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+            }
+        }
+    }
+
+    void stopTracking() {
+        mTracking = false;
+        mVelocityTracker.recycle();
+        mVelocityTracker = null;
+    }
+
+    void incrementAnim() {
+        long now = SystemClock.uptimeMillis();
+        float t = ((float)(now - mAnimLastTime)) / 1000;            // ms -> s
+        final float y = mAnimY;
+        final float v = mAnimVel;                                   // px/s
+        final float a = mAnimAccel;                                 // px/s/s
+        mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
+        mAnimVel = v + (a*t);                                       // px/s
+        mAnimLastTime = now;                                        // ms
+        //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
+        //        + " mAnimAccel=" + mAnimAccel);
+    }
+
+    void doRevealAnimation() {
+        final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
+        if (mAnimatingReveal && mAnimating && mAnimY < h) {
+            incrementAnim();
+            if (mAnimY >= h) {
+                mAnimY = h;
+                updateExpandedViewPos((int)mAnimY);
+            } else {
+                updateExpandedViewPos((int)mAnimY);
+                mCurAnimationTime += ANIM_FRAME_DURATION;
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+                        mCurAnimationTime);
+            }
+        }
+    }
+    
+    void prepareTracking(int y, boolean opening) {
+        mTracking = true;
+        mVelocityTracker = VelocityTracker.obtain();
+        if (opening) {
+            mAnimAccel = 2000.0f;
+            mAnimVel = 200;
+            mAnimY = mStatusBarView.getHeight();
+            updateExpandedViewPos((int)mAnimY);
+            mAnimating = true;
+            mAnimatingReveal = true;
+            mHandler.removeMessages(MSG_ANIMATE);
+            mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+            long now = SystemClock.uptimeMillis();
+            mAnimLastTime = now;
+            mCurAnimationTime = now + ANIM_FRAME_DURATION;
+            mAnimating = true;
+            mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+                    mCurAnimationTime);
+            makeExpandedVisible();
+        } else {
+            // it's open, close it?
+            if (mAnimating) {
+                mAnimating = false;
+                mHandler.removeMessages(MSG_ANIMATE);
+            }
+            updateExpandedViewPos(y + mViewDelta);
+        }
+    }
+    
+    void performFling(int y, float vel, boolean always) {
+        mAnimatingReveal = false;
+        mDisplayHeight = mDisplay.getHeight();
+
+        mAnimY = y;
+        mAnimVel = vel;
+
+        //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
+
+        if (mExpanded) {
+            if (!always && (
+                    vel > 200.0f
+                    || (y > (mDisplayHeight-25) && vel > -200.0f))) {
+                // We are expanded, but they didn't move sufficiently to cause
+                // us to retract.  Animate back to the expanded position.
+                mAnimAccel = 2000.0f;
+                if (vel < 0) {
+                    mAnimVel = 0;
+                }
+            }
+            else {
+                // We are expanded and are now going to animate away.
+                mAnimAccel = -2000.0f;
+                if (vel > 0) {
+                    mAnimVel = 0;
+                }
+            }
+        } else {
+            if (always || (
+                    vel > 200.0f
+                    || (y > (mDisplayHeight/2) && vel > -200.0f))) {
+                // We are collapsed, and they moved enough to allow us to
+                // expand.  Animate in the notifications.
+                mAnimAccel = 2000.0f;
+                if (vel < 0) {
+                    mAnimVel = 0;
+                }
+            }
+            else {
+                // We are collapsed, but they didn't move sufficiently to cause
+                // us to retract.  Animate back to the collapsed position.
+                mAnimAccel = -2000.0f;
+                if (vel > 0) {
+                    mAnimVel = 0;
+                }
+            }
+        }
+        //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
+        //        + " mAnimAccel=" + mAnimAccel);
+
+        long now = SystemClock.uptimeMillis();
+        mAnimLastTime = now;
+        mCurAnimationTime = now + ANIM_FRAME_DURATION;
+        mAnimating = true;
+        mHandler.removeMessages(MSG_ANIMATE);
+        mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+        stopTracking();
+    }
+    
+    boolean interceptTouchEvent(MotionEvent event) {
+        if (SPEW) {
+            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+                + mDisabled);
+        }
+
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+            return false;
+        }
+        
+        final int statusBarSize = mStatusBarView.getHeight();
+        final int hitSize = statusBarSize*2;
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            final int y = (int)event.getRawY();
+
+            if (!mExpanded) {
+                mViewDelta = statusBarSize - y;
+            } else {
+                mTrackingView.getLocationOnScreen(mAbsPos);
+                mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
+            }
+            if ((!mExpanded && y < hitSize) ||
+                    (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
+
+                // We drop events at the edge of the screen to make the windowshade come
+                // down by accident less, especially when pushing open a device with a keyboard
+                // that rotates (like g1 and droid)
+                int x = (int)event.getRawX();
+                final int edgeBorder = mEdgeBorder;
+                if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
+                    prepareTracking(y, !mExpanded);// opening if we're not already fully visible
+                    mVelocityTracker.addMovement(event);
+                }
+            }
+        } else if (mTracking) {
+            mVelocityTracker.addMovement(event);
+            final int minY = statusBarSize + mCloseView.getHeight();
+            if (event.getAction() == MotionEvent.ACTION_MOVE) {
+                int y = (int)event.getRawY();
+                if (mAnimatingReveal && y < minY) {
+                    // nothing
+                } else  {
+                    mAnimatingReveal = false;
+                    updateExpandedViewPos(y + mViewDelta);
+                }
+            } else if (event.getAction() == MotionEvent.ACTION_UP) {
+                mVelocityTracker.computeCurrentVelocity(1000);
+
+                float yVel = mVelocityTracker.getYVelocity();
+                boolean negative = yVel < 0;
+
+                float xVel = mVelocityTracker.getXVelocity();
+                if (xVel < 0) {
+                    xVel = -xVel;
+                }
+                if (xVel > 150.0f) {
+                    xVel = 150.0f; // limit how much we care about the x axis
+                }
+
+                float vel = (float)Math.hypot(yVel, xVel);
+                if (negative) {
+                    vel = -vel;
+                }
+                
+                performFling((int)event.getRawY(), vel, false);
+            }
+            
+        }
+        return false;
+    }
+
+    private class Launcher implements View.OnClickListener {
+        private PendingIntent mIntent;
+        private String mPkg;
+        private String mTag;
+        private int mId;
+
+        Launcher(PendingIntent intent, String pkg, String tag, int id) {
+            mIntent = intent;
+            mPkg = pkg;
+            mTag = tag;
+            mId = id;
+        }
+
+        public void onClick(View v) {
+            try {
+                // The intent we are sending is for the application, which
+                // won't have permission to immediately start an activity after
+                // the user switches to home.  We know it is safe to do at this
+                // point, so make sure new activity switches are now allowed.
+                ActivityManagerNative.getDefault().resumeAppSwitches();
+            } catch (RemoteException e) {
+            }
+            int[] pos = new int[2];
+            v.getLocationOnScreen(pos);
+            Intent overlay = new Intent();
+            overlay.setSourceBounds(
+                    new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+            try {
+                mIntent.send(PhoneStatusBarService.this, 0, overlay);
+                mNotificationCallbacks.onNotificationClick(mPkg, mTag, mId);
+            } catch (PendingIntent.CanceledException e) {
+                // the stack trace isn't very helpful here.  Just log the exception message.
+                Slog.w(TAG, "Sending contentIntent failed: " + e);
+            }
+            deactivate();
+        }
+    }
+
+    private class MyTicker extends Ticker {
+        MyTicker(Context context, StatusBarView sb) {
+            super(context, sb);
+        }
+        
+        @Override
+        void tickerStarting() {
+            mTicking = true;
+            mIcons.setVisibility(View.GONE);
+            mTickerView.setVisibility(View.VISIBLE);
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
+            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
+            if (mExpandedVisible) {
+                setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
+            }
+        }
+
+        @Override
+        void tickerDone() {
+            mIcons.setVisibility(View.VISIBLE);
+            mTickerView.setVisibility(View.GONE);
+            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
+                        mTickingDoneListener));
+            if (mExpandedVisible) {
+                setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
+            }
+        }
+
+        void tickerHalting() {
+            mIcons.setVisibility(View.VISIBLE);
+            mTickerView.setVisibility(View.GONE);
+            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
+                        mTickingDoneListener));
+            if (mExpandedVisible) {
+                setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+            }
+        }
+    }
+
+    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
+        public void onAnimationEnd(Animation animation) {
+            mTicking = false;
+        }
+        public void onAnimationRepeat(Animation animation) {
+        }
+        public void onAnimationStart(Animation animation) {
+        }
+    };
+
+    private Animation loadAnim(int id, Animation.AnimationListener listener) {
+        Animation anim = AnimationUtils.loadAnimation(PhoneStatusBarService.this, id);
+        if (listener != null) {
+            anim.setAnimationListener(listener);
+        }
+        return anim;
+    }
+
+    public String viewInfo(View v) {
+        return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
+                + " " + v.getWidth() + "x" + v.getHeight() + ")";
+    }
+
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump StatusBar from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+        
+        synchronized (mQueueLock) {
+            pw.println("Current Status Bar state:");
+            pw.println("  mExpanded=" + mExpanded
+                    + ", mExpandedVisible=" + mExpandedVisible);
+            pw.println("  mTicking=" + mTicking);
+            pw.println("  mTracking=" + mTracking);
+            pw.println("  mAnimating=" + mAnimating
+                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
+                    + ", mAnimAccel=" + mAnimAccel);
+            pw.println("  mCurAnimationTime=" + mCurAnimationTime
+                    + " mAnimLastTime=" + mAnimLastTime);
+            pw.println("  mDisplayHeight=" + mDisplayHeight
+                    + " mAnimatingReveal=" + mAnimatingReveal
+                    + " mViewDelta=" + mViewDelta);
+            pw.println("  mDisplayHeight=" + mDisplayHeight);
+            final int N = mQueue.size();
+            pw.println("  mQueue.size=" + N);
+            for (int i=0; i<N; i++) {
+                PendingOp op = mQueue.get(i);
+                pw.println("    [" + i + "] key=" + op.key + " code=" + op.code + " visible="
+                        + op.visible);
+                pw.println("           iconData=" + op.iconData);
+                pw.println("           notificationData=" + op.notificationData);
+            }
+            pw.println("  mExpandedParams: " + mExpandedParams);
+            pw.println("  mExpandedView: " + viewInfo(mExpandedView));
+            pw.println("  mExpandedDialog: " + mExpandedDialog);
+            pw.println("  mTrackingParams: " + mTrackingParams);
+            pw.println("  mTrackingView: " + viewInfo(mTrackingView));
+            pw.println("  mOngoingTitle: " + viewInfo(mOngoingTitle));
+            pw.println("  mOngoingItems: " + viewInfo(mOngoingItems));
+            pw.println("  mLatestTitle: " + viewInfo(mLatestTitle));
+            pw.println("  mLatestItems: " + viewInfo(mLatestItems));
+            pw.println("  mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
+            pw.println("  mCloseView: " + viewInfo(mCloseView));
+            pw.println("  mTickerView: " + viewInfo(mTickerView));
+            pw.println("  mScrollView: " + viewInfo(mScrollView)
+                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
+            pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
+        }
+        synchronized (mIconMap) {
+            final int N = mIconMap.size();
+            pw.println("  mIconMap.size=" + N);
+            Set<IBinder> keys = mIconMap.keySet();
+            int i=0;
+            for (IBinder key: keys) {
+                StatusBarIcon icon = mIconMap.get(key);
+                pw.println("    [" + i + "] key=" + key);
+                pw.println("           data=" + icon.mData);
+                i++;
+            }
+        }
+        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 (mDisableRecords) {
+            final int N = mDisableRecords.size();
+            pw.println("  mDisableRecords.size=" + N
+                    + " mDisabled=0x" + Integer.toHexString(mDisabled));
+            for (int i=0; i<N; i++) {
+                DisableRecord tok = mDisableRecords.get(i);
+                pw.println("    [" + i + "] what=0x" + Integer.toHexString(tok.what)
+                                + " pkg=" + tok.pkg + " token=" + tok.token);
+            }
+        }
+        
+        if (false) {
+            pw.println("see the logcat for a dump of the views we have created.");
+            // must happen on ui thread
+            mHandler.post(new Runnable() {
+                    public void run() {
+                        mStatusBarView.getLocationOnScreen(mAbsPos);
+                        Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+                                + ") " + mStatusBarView.getWidth() + "x"
+                                + mStatusBarView.getHeight());
+                        mStatusBarView.debug();
+
+                        mExpandedView.getLocationOnScreen(mAbsPos);
+                        Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+                                + ") " + mExpandedView.getWidth() + "x"
+                                + mExpandedView.getHeight());
+                        mExpandedView.debug();
+
+                        mTrackingView.getLocationOnScreen(mAbsPos);
+                        Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+                                + ") " + mTrackingView.getWidth() + "x"
+                                + mTrackingView.getHeight());
+                        mTrackingView.debug();
+                    }
+                });
+        }
+    }
+
+    void onBarViewAttached() {
+        WindowManager.LayoutParams lp;
+        int pixelFormat;
+        Drawable bg;
+
+        /// ---------- Tracking View --------------
+        pixelFormat = PixelFormat.RGBX_8888;
+        bg = mTrackingView.getBackground();
+        if (bg != null) {
+            pixelFormat = bg.getOpacity();
+        }
+
+        lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                pixelFormat);
+//        lp.token = mStatusBarView.getWindowToken();
+        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.setTitle("TrackingView");
+        lp.y = mTrackingPosition;
+        mTrackingParams = lp;
+
+        WindowManagerImpl.getDefault().addView(mTrackingView, lp);
+    }
+
+    void onTrackingViewAttached() {
+        WindowManager.LayoutParams lp;
+        int pixelFormat;
+        Drawable bg;
+
+        /// ---------- Expanded View --------------
+        pixelFormat = PixelFormat.TRANSLUCENT;
+
+        final int disph = mDisplay.getHeight();
+        lp = mExpandedDialog.getWindow().getAttributes();
+        lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
+        lp.height = getExpandedHeight();
+        lp.x = 0;
+        mTrackingPosition = lp.y = -disph; // sufficiently large negative
+        lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+        lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_DITHER
+                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        lp.format = pixelFormat;
+        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.setTitle("StatusBarExpanded");
+        mExpandedDialog.getWindow().setAttributes(lp);
+        mExpandedDialog.getWindow().setFormat(pixelFormat);
+        mExpandedParams = lp;
+
+        mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+        mExpandedDialog.setContentView(mExpandedView,
+                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                                           ViewGroup.LayoutParams.MATCH_PARENT));
+        mExpandedDialog.getWindow().setBackgroundDrawable(null);
+        mExpandedDialog.show();
+        FrameLayout hack = (FrameLayout)mExpandedView.getParent();
+    }
+
+    void setDateViewVisibility(boolean visible, int anim) {
+        mDateView.setUpdates(visible);
+        mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+        mDateView.startAnimation(loadAnim(anim, null));
+    }
+
+    void setNotificationIconVisibility(boolean visible, int anim) {
+        int old = mNotificationIcons.getVisibility();
+        int v = visible ? View.VISIBLE : View.INVISIBLE;
+        if (old != v) {
+            mNotificationIcons.setVisibility(v);
+            mNotificationIcons.startAnimation(loadAnim(anim, null));
+        }
+    }
+
+    void updateExpandedViewPos(int expandedPosition) {
+        if (SPEW) {
+            Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
+                    + " mTrackingParams.y=" + mTrackingParams.y
+                    + " mTrackingPosition=" + mTrackingPosition);
+        }
+
+        int h = mStatusBarView.getHeight();
+        int disph = mDisplay.getHeight();
+
+        // If the expanded view is not visible, make sure they're still off screen.
+        // Maybe the view was resized.
+        if (!mExpandedVisible) {
+            if (mTrackingView != null) {
+                mTrackingPosition = -disph;
+                if (mTrackingParams != null) {
+                    mTrackingParams.y = mTrackingPosition;
+                    WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+                }
+            }
+            if (mExpandedParams != null) {
+                mExpandedParams.y = -disph;
+                mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+            }
+            return;
+        }
+
+        // tracking view...
+        int pos;
+        if (expandedPosition == EXPANDED_FULL_OPEN) {
+            pos = h;
+        }
+        else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
+            pos = mTrackingPosition;
+        }
+        else {
+            if (expandedPosition <= disph) {
+                pos = expandedPosition;
+            } else {
+                pos = disph;
+            }
+            pos -= disph-h;
+        }
+        mTrackingPosition = mTrackingParams.y = pos;
+        mTrackingParams.height = disph-h;
+        WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+
+        if (mExpandedParams != null) {
+            mCloseView.getLocationInWindow(mPositionTmp);
+            final int closePos = mPositionTmp[1];
+
+            mExpandedContents.getLocationInWindow(mPositionTmp);
+            final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
+
+            mExpandedParams.y = pos + mTrackingView.getHeight()
+                    - (mTrackingParams.height-closePos) - contentsBottom;
+            int max = h;
+            if (mExpandedParams.y > max) {
+                mExpandedParams.y = max;
+            }
+            int min = mTrackingPosition;
+            if (mExpandedParams.y < min) {
+                mExpandedParams.y = min;
+            }
+
+            boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
+            if (!visible) {
+                // if the contents aren't visible, move the expanded view way off screen
+                // because the window itself extends below the content view.
+                mExpandedParams.y = -disph;
+            }
+            panelSlightlyVisible(visible);
+            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        }
+
+        if (SPEW) {
+            Slog.d(TAG, "updateExpandedViewPos after  expandedPosition=" + expandedPosition
+                    + " mTrackingParams.y=" + mTrackingParams.y
+                    + " mTrackingPosition=" + mTrackingPosition
+                    + " mExpandedParams.y=" + mExpandedParams.y
+                    + " mExpandedParams.height=" + mExpandedParams.height);
+        }
+    }
+
+    int getExpandedHeight() {
+        return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
+    }
+
+    void updateExpandedHeight() {
+        if (mExpandedView != null) {
+            mExpandedParams.height = getExpandedHeight();
+            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        }
+    }
+
+    /**
+     * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+     * This was added last-minute and is inconsistent with the way the rest of the notifications
+     * are handled, because the notification isn't really cancelled.  The lights are just
+     * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
+     * this is what he wants. (see bug 1131461)
+     */
+    private boolean mPanelSlightlyVisible;
+    void panelSlightlyVisible(boolean visible) {
+        if (mPanelSlightlyVisible != visible) {
+            mPanelSlightlyVisible = visible;
+            if (visible) {
+                // tell the notification manager to turn off the lights.
+                mNotificationCallbacks.onPanelRevealed();
+            }
+        }
+    }
+
+    void performDisableActions(int net) {
+        int old = mDisabled;
+        int diff = net ^ old;
+        mDisabled = net;
+
+        // act accordingly
+        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
+                Slog.d(TAG, "DISABLE_EXPAND: yes");
+                animateCollapse();
+            }
+        }
+        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+                if (mTicking) {
+                    mNotificationIcons.setVisibility(View.INVISIBLE);
+                    mTicker.halt();
+                } else {
+                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+                }
+            } else {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+                if (!mExpandedVisible) {
+                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+                }
+            }
+        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+                mTicker.halt();
+            }
+        }
+    }
+
+    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            mNotificationCallbacks.onClearAll();
+            addPendingOp(OP_EXPAND, null, false);
+        }
+    };
+
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+                    || Intent.ACTION_SCREEN_OFF.equals(action)) {
+                deactivate();
+            }
+            else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
+                updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
+                        intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
+                        intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
+                        intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
+            }
+            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+                updateResources();
+            }
+        }
+    };
+
+    void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
+        if (false) {
+            Slog.d(TAG, "updateNetworkName showSpn=" + showSpn + " spn=" + spn
+                    + " showPlmn=" + showPlmn + " plmn=" + plmn);
+        }
+        boolean something = false;
+        if (showPlmn) {
+            mPlmnLabel.setVisibility(View.VISIBLE);
+            if (plmn != null) {
+                mPlmnLabel.setText(plmn);
+            } else {
+                mPlmnLabel.setText(com.android.internal.R.string.lockscreen_carrier_default);
+            }
+        } else {
+            mPlmnLabel.setText("");
+            mPlmnLabel.setVisibility(View.GONE);
+        }
+        if (showSpn && spn != null) {
+            mSpnLabel.setText(spn);
+            mSpnLabel.setVisibility(View.VISIBLE);
+            something = true;
+        } else {
+            mSpnLabel.setText("");
+            mSpnLabel.setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * Reload some of our resources when the configuration changes.
+     * 
+     * We don't reload everything when the configuration changes -- we probably
+     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
+     * meantime, just update the things that we know change.
+     */
+    void updateResources() {
+        Resources res = getResources();
+
+        mClearButton.setText(getText(R.string.status_bar_clear_all_button));
+        mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
+        mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
+        mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
+
+        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+        if (false) Slog.v(TAG, "updateResources");
+    }
+
+    //
+    // tracing
+    //
+
+    void postStartTracing() {
+        mHandler.postDelayed(mStartTracing, 3000);
+    }
+
+    void vibrate() {
+        android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
+        vib.vibrate(250);
+    }
+
+    Runnable mStartTracing = new Runnable() {
+        public void run() {
+            vibrate();
+            SystemClock.sleep(250);
+            Slog.d(TAG, "startTracing");
+            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
+            mHandler.postDelayed(mStopTracing, 10000);
+        }
+    };
+
+    Runnable mStopTracing = new Runnable() {
+        public void run() {
+            android.os.Debug.stopMethodTracing();
+            Slog.d(TAG, "stopTracing");
+            vibrate();
+        }
+    };
+    
+    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");
+            PhoneStatusBarService.this.registerReceiver(this, filter);
+            IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+            PhoneStatusBarService.this.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 (PhoneStatusBarService.this) {
+                    for (String pkg : pkgList) {
+                        list = mNotificationData.notificationsForPackage(pkg);
+                    }
+                }
+            }
+            
+            if (list != null) {
+                final int N = list.size();
+                for (int i=0; i<N; i++) {
+                    removeIcon(list.get(i).key);
+                }
+            }
+        }
+    }
 }
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarException.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarException.java
new file mode 100644
index 0000000..1bb92f0
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarException.java
@@ -0,0 +1,23 @@
+/*
+ * 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.policy.statusbar.phone;
+
+public class StatusBarException extends RuntimeException {
+    StatusBarException(String msg) {
+        super(msg);
+    }
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarIcon.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarIcon.java
new file mode 100644
index 0000000..b48a17b
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarIcon.java
@@ -0,0 +1,186 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class StatusBarIcon {
+    // TODO: get this from a resource
+    private static final int ICON_GAP = 8;
+    private static final int ICON_WIDTH = 25;
+    private static final int ICON_HEIGHT = 25;
+
+    public View view;
+
+    IconData mData;
+    
+    private TextView mTextView;
+    private AnimatedImageView mImageView;
+    private TextView mNumberView;
+
+    public StatusBarIcon(Context context, IconData data, ViewGroup parent) {
+        mData = data.clone();
+
+        switch (data.type) {
+            case IconData.TEXT: {
+                TextView t;
+                t = new TextView(context);
+                mTextView = t;
+                LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.WRAP_CONTENT,
+                        LinearLayout.LayoutParams.MATCH_PARENT);
+                t.setTextSize(16);
+                t.setTextColor(0xff000000);
+                t.setTypeface(Typeface.DEFAULT_BOLD);
+                t.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
+                t.setPadding(6, 0, 0, 0);
+                t.setLayoutParams(layoutParams);
+                t.setText(data.text);
+                this.view = t;
+                break;
+            }
+
+            case IconData.ICON: {
+                // container
+                LayoutInflater inflater = (LayoutInflater)context.getSystemService(
+                                                Context.LAYOUT_INFLATER_SERVICE);
+                View v = inflater.inflate(com.android.internal.R.layout.status_bar_icon, parent, false);
+                this.view = v;
+
+                // icon
+                AnimatedImageView im = (AnimatedImageView)v.findViewById(com.android.internal.R.id.image);
+                im.setImageDrawable(getIcon(context, data));
+                im.setImageLevel(data.iconLevel);
+                mImageView = im;
+
+                // number
+                TextView nv = (TextView)v.findViewById(com.android.internal.R.id.number);
+                mNumberView = nv;
+                if (data.number > 0) {
+                    nv.setText("" + data.number);
+                    nv.setVisibility(View.VISIBLE);
+                } else {
+                    nv.setVisibility(View.GONE);
+                }
+                break;
+            }
+        }
+    }
+
+    public void update(Context context, IconData data) throws StatusBarException {
+        if (mData.type != data.type) {
+            throw new StatusBarException("status bar entry type can't change");
+        }
+        switch (data.type) {
+        case IconData.TEXT:
+            if (!TextUtils.equals(mData.text, data.text)) {
+                TextView tv = mTextView;
+                tv.setText(data.text);
+            }
+            break;
+        case IconData.ICON:
+            if (((mData.iconPackage != null && data.iconPackage != null)
+                        && !mData.iconPackage.equals(data.iconPackage))
+                    || mData.iconId != data.iconId
+                    || mData.iconLevel != data.iconLevel) {
+                ImageView im = mImageView;
+                im.setImageDrawable(getIcon(context, data));
+                im.setImageLevel(data.iconLevel);
+            }
+            if (mData.number != data.number) {
+                TextView nv = mNumberView;
+                if (data.number > 0) {
+                    nv.setText("" + data.number);
+                } else {
+                    nv.setText("");
+                }
+            }
+            break;
+        }
+        mData.copyFrom(data);
+    }
+
+    public void update(int number) {
+        if (mData.number != number) {
+            TextView nv = mNumberView;
+            if (number > 0) {
+                nv.setText("" + number);
+            } else {
+                nv.setText("");
+            }
+        }
+        mData.number = number;
+    }
+
+
+    /**
+     * Returns the right icon to use for this item, respecting the iconId and
+     * iconPackage (if set)
+     * 
+     * @param context Context to use to get resources if iconPackage is not set
+     * @return Drawable for this item, or null if the package or item could not
+     *         be found
+     */
+    static Drawable getIcon(Context context, IconData data) {
+
+        Resources r = null;
+
+        if (data.iconPackage != null) {
+            try {
+                r = context.getPackageManager().getResourcesForApplication(data.iconPackage);
+            } catch (PackageManager.NameNotFoundException ex) {
+                Slog.e(PhoneStatusBarService.TAG, "Icon package not found: " + data.iconPackage, ex);
+                return null;
+            }
+        } else {
+            r = context.getResources();
+        }
+
+        if (data.iconId == 0) {
+            Slog.w(PhoneStatusBarService.TAG, "No icon ID for slot " + data.slot);
+            return null;
+        }
+        
+        try {
+            return r.getDrawable(data.iconId);
+        } catch (RuntimeException e) {
+            Slog.w(PhoneStatusBarService.TAG, "Icon not found in "
+                  + (data.iconPackage != null ? data.iconId : "<system>")
+                  + ": " + Integer.toHexString(data.iconId));
+        }
+
+        return null;
+    }
+
+    int getNumber() {
+        return mData.number;
+    }
+}
+
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarNotification.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarNotification.java
new file mode 100644
index 0000000..97f77da
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarNotification.java
@@ -0,0 +1,27 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.os.IBinder;
+import android.view.View;
+
+public class StatusBarNotification {
+    IBinder key;
+    NotificationData data;
+    View view;
+    View contentView;
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarPolicy.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarPolicy.java
new file mode 100644
index 0000000..5cc8482
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarPolicy.java
@@ -0,0 +1,1386 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.app.AlertDialog;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothPbap;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.TypedArray;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.text.format.DateFormat;
+import android.text.style.RelativeSizeSpan;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.util.Slog;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.cdma.EriInfo;
+import com.android.internal.telephony.cdma.TtyIntent;
+import com.android.server.am.BatteryStatsService;
+
+import com.android.server.status.IconData;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * This class contains all of the policy about which icons are installed in the status
+ * bar at boot time.  In reality, it should go into the android.policy package, but
+ * putting it here is the first step from extracting it.
+ */
+public class StatusBarPolicy {
+    private static final String TAG = "StatusBarPolicy";
+
+    private static StatusBarPolicy sInstance;
+
+    // message codes for the handler
+    private static final int EVENT_BATTERY_CLOSE = 4;
+
+    private final Context mContext;
+    private final StatusBarService mService;
+    private final Handler mHandler = new StatusBarHandler();
+    private final IBatteryStats mBatteryStats;
+
+    // clock
+    private Calendar mCalendar;
+    private String mClockFormatString;
+    private SimpleDateFormat mClockFormat;
+    private IBinder mClockIcon;
+    private IconData mClockData;
+
+    // storage
+    private StorageManager mStorageManager;
+
+    // battery
+    private IBinder mBatteryIcon;
+    private IconData mBatteryData;
+    private boolean mBatteryFirst = true;
+    private boolean mBatteryPlugged;
+    private int mBatteryLevel;
+    private AlertDialog mLowBatteryDialog;
+    private TextView mBatteryLevelTextView;
+    private View mBatteryView;
+    private int mBatteryViewSequence;
+    private boolean mBatteryShowLowOnEndCall = false;
+    private static final boolean SHOW_LOW_BATTERY_WARNING = true;
+    private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true;
+
+    // phone
+    private TelephonyManager mPhone;
+    private IBinder mPhoneIcon;
+
+    //***** Signal strength icons
+    private IconData mPhoneData;
+    //GSM/UMTS
+    private static final int[] sSignalImages = new int[] {
+        com.android.internal.R.drawable.stat_sys_signal_0,
+        com.android.internal.R.drawable.stat_sys_signal_1,
+        com.android.internal.R.drawable.stat_sys_signal_2,
+        com.android.internal.R.drawable.stat_sys_signal_3,
+        com.android.internal.R.drawable.stat_sys_signal_4
+    };
+    private static final int[] sSignalImages_r = new int[] {
+        com.android.internal.R.drawable.stat_sys_r_signal_0,
+        com.android.internal.R.drawable.stat_sys_r_signal_1,
+        com.android.internal.R.drawable.stat_sys_r_signal_2,
+        com.android.internal.R.drawable.stat_sys_r_signal_3,
+        com.android.internal.R.drawable.stat_sys_r_signal_4
+    };
+    private static final int[] sRoamingIndicatorImages_cdma = new int[] {
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator
+        // 1 is Standard Roaming Indicator OFF
+        // TODO T: image never used, remove and put 0 instead?
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+        // 2 is Standard Roaming Indicator FLASHING
+        // TODO T: image never used, remove and put 0 instead?
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+        // 3-12 Standard ERI
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //3
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+        // 13-63 Reserved for Standard ERI
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //13
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+        // 64-127 Reserved for Non Standard (Operator Specific) ERI
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //64
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0 //83
+
+        // 128-255 Reserved
+    };
+
+    //***** Data connection icons
+    private int[] mDataIconList = sDataNetType_g;
+    //GSM/UMTS
+    private static final int[] sDataNetType_g = new int[] {
+            com.android.internal.R.drawable.stat_sys_data_connected_g,
+            com.android.internal.R.drawable.stat_sys_data_in_g,
+            com.android.internal.R.drawable.stat_sys_data_out_g,
+            com.android.internal.R.drawable.stat_sys_data_inandout_g,
+        };
+    private static final int[] sDataNetType_3g = new int[] {
+            com.android.internal.R.drawable.stat_sys_data_connected_3g,
+            com.android.internal.R.drawable.stat_sys_data_in_3g,
+            com.android.internal.R.drawable.stat_sys_data_out_3g,
+            com.android.internal.R.drawable.stat_sys_data_inandout_3g,
+        };
+    private static final int[] sDataNetType_e = new int[] {
+            com.android.internal.R.drawable.stat_sys_data_connected_e,
+            com.android.internal.R.drawable.stat_sys_data_in_e,
+            com.android.internal.R.drawable.stat_sys_data_out_e,
+            com.android.internal.R.drawable.stat_sys_data_inandout_e,
+        };
+    //3.5G
+    private static final int[] sDataNetType_h = new int[] {
+            com.android.internal.R.drawable.stat_sys_data_connected_h,
+            com.android.internal.R.drawable.stat_sys_data_in_h,
+            com.android.internal.R.drawable.stat_sys_data_out_h,
+            com.android.internal.R.drawable.stat_sys_data_inandout_h,
+    };
+
+    //CDMA
+    // Use 3G icons for EVDO data and 1x icons for 1XRTT data
+    private static final int[] sDataNetType_1x = new int[] {
+        com.android.internal.R.drawable.stat_sys_data_connected_1x,
+        com.android.internal.R.drawable.stat_sys_data_in_1x,
+        com.android.internal.R.drawable.stat_sys_data_out_1x,
+        com.android.internal.R.drawable.stat_sys_data_inandout_1x,
+    };
+
+    // Assume it's all good unless we hear otherwise.  We don't always seem
+    // to get broadcasts that it *is* there.
+    IccCard.State mSimState = IccCard.State.READY;
+    int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
+    int mDataState = TelephonyManager.DATA_DISCONNECTED;
+    int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
+    ServiceState mServiceState;
+    SignalStrength mSignalStrength;
+
+    // data connection
+    private IBinder mDataIcon;
+    private IconData mDataData;
+    private boolean mDataIconVisible;
+    private boolean mHspaDataDistinguishable;
+
+    // ringer volume
+    private IBinder mVolumeIcon;
+    private IconData mVolumeData;
+    private boolean mVolumeVisible;
+
+    // bluetooth device status
+    private IBinder mBluetoothIcon;
+    private IconData mBluetoothData;
+    private int mBluetoothHeadsetState;
+    private boolean mBluetoothA2dpConnected;
+    private int mBluetoothPbapState;
+    private boolean mBluetoothEnabled;
+
+    // wifi
+    private static final int[] sWifiSignalImages = new int[] {
+            com.android.internal.R.drawable.stat_sys_wifi_signal_1,
+            com.android.internal.R.drawable.stat_sys_wifi_signal_2,
+            com.android.internal.R.drawable.stat_sys_wifi_signal_3,
+            com.android.internal.R.drawable.stat_sys_wifi_signal_4,
+        };
+    private static final int sWifiTemporarilyNotConnectedImage =
+            com.android.internal.R.drawable.stat_sys_wifi_signal_0;
+
+    private int mLastWifiSignalLevel = -1;
+    private boolean mIsWifiConnected = false;
+    private IBinder mWifiIcon;
+    private IconData mWifiData;
+
+    // gps
+    private IBinder mGpsIcon;
+    private IconData mGpsEnabledIconData;
+    private IconData mGpsFixIconData;
+
+    // alarm clock
+    // Icon lit when clock is set
+    private IBinder mAlarmClockIcon;
+    private IconData mAlarmClockIconData;
+
+    // sync state
+    // If sync is active the SyncActive icon is displayed. If sync is not active but
+    // sync is failing the SyncFailing icon is displayed. Otherwise neither are displayed.
+    private IBinder mSyncActiveIcon;
+    private IBinder mSyncFailingIcon;
+
+    // TTY mode
+    // Icon lit when TTY mode is enabled
+    private IBinder mTTYModeIcon;
+    private IconData mTTYModeEnableIconData;
+
+    // Cdma Roaming Indicator, ERI
+    private IBinder mCdmaRoamingIndicatorIcon;
+    private IconData mCdmaRoamingIndicatorIconData;
+
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_TIME_TICK)) {
+                updateClock();
+            }
+            else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
+                updateClock();
+            }
+            else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+                updateBattery(intent);
+            }
+            else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+                updateClock();
+            }
+            else if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
+                String tz = intent.getStringExtra("time-zone");
+                mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
+                updateClock();
+            }
+            else if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
+                updateAlarm(intent);
+            }
+            else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
+                updateSyncState(intent);
+            }
+            else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
+                onBatteryLow(intent);
+            }
+            else if (action.equals(Intent.ACTION_BATTERY_OKAY)
+                    || action.equals(Intent.ACTION_POWER_CONNECTED)) {
+                onBatteryOkay(intent);
+            }
+            else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
+                    action.equals(BluetoothHeadset.ACTION_STATE_CHANGED) ||
+                    action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED) ||
+                    action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
+                updateBluetooth(intent);
+            }
+            else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
+                    action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) ||
+                    action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+                updateWifi(intent);
+            }
+            else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) ||
+                    action.equals(LocationManager.GPS_FIX_CHANGE_ACTION)) {
+                updateGps(intent);
+            }
+            else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
+                    action.equals(AudioManager.VIBRATE_SETTING_CHANGED_ACTION)) {
+                updateVolume();
+            }
+            else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+                updateSimState(intent);
+            }
+            else if (action.equals(TtyIntent.TTY_ENABLED_CHANGE_ACTION)) {
+                updateTTY(intent);
+            }
+        }
+    };
+
+    private StatusBarPolicy(Context context, StatusBarService service) {
+        mContext = context;
+        mService = service;
+        mSignalStrength = new SignalStrength();
+        mBatteryStats = BatteryStatsService.getService();
+
+        // clock
+        mCalendar = Calendar.getInstance(TimeZone.getDefault());
+        mClockData = IconData.makeText("clock", "");
+        mClockIcon = service.addIcon(mClockData, null);
+        updateClock();
+
+        // storage
+        mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+        mStorageManager.registerListener(
+                new com.android.server.status.StorageNotification(context));
+
+        // battery
+        mBatteryData = IconData.makeIcon("battery",
+                null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 0);
+        mBatteryIcon = service.addIcon(mBatteryData, null);
+
+        // phone_signal
+        mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+        mPhoneData = IconData.makeIcon("phone_signal",
+                null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
+        mPhoneIcon = service.addIcon(mPhoneData, null);
+
+        // register for phone state notifications.
+        ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
+                .listen(mPhoneStateListener,
+                          PhoneStateListener.LISTEN_SERVICE_STATE
+                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+                        | PhoneStateListener.LISTEN_CALL_STATE
+                        | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+                        | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+
+        // data_connection
+        mDataData = IconData.makeIcon("data_connection",
+                null, com.android.internal.R.drawable.stat_sys_data_connected_g, 0, 0);
+        mDataIcon = service.addIcon(mDataData, null);
+        service.setIconVisibility(mDataIcon, false);
+
+        // wifi
+        mWifiData = IconData.makeIcon("wifi", null, sWifiSignalImages[0], 0, 0);
+        mWifiIcon = service.addIcon(mWifiData, null);
+        service.setIconVisibility(mWifiIcon, false);
+        // wifi will get updated by the sticky intents
+
+        // TTY status
+        mTTYModeEnableIconData = IconData.makeIcon("tty",
+                null, com.android.internal.R.drawable.stat_sys_tty_mode, 0, 0);
+        mTTYModeIcon = service.addIcon(mTTYModeEnableIconData, null);
+        service.setIconVisibility(mTTYModeIcon, false);
+
+        // Cdma Roaming Indicator, ERI
+        mCdmaRoamingIndicatorIconData = IconData.makeIcon("cdma_eri",
+                null, com.android.internal.R.drawable.stat_sys_roaming_cdma_0, 0, 0);
+        mCdmaRoamingIndicatorIcon = service.addIcon(mCdmaRoamingIndicatorIconData, null);
+        service.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+
+        // bluetooth status
+        mBluetoothData = IconData.makeIcon("bluetooth",
+                null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
+        mBluetoothIcon = service.addIcon(mBluetoothData, null);
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            mBluetoothEnabled = adapter.isEnabled();
+        } else {
+            mBluetoothEnabled = false;
+        }
+        mBluetoothA2dpConnected = false;
+        mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
+        mBluetoothPbapState = BluetoothPbap.STATE_DISCONNECTED;
+        mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
+
+        // Gps status
+        mGpsEnabledIconData = IconData.makeIcon("gps",
+                null, com.android.internal.R.drawable.stat_sys_gps_acquiring_anim, 0, 0);
+        mGpsFixIconData = IconData.makeIcon("gps",
+                null, com.android.internal.R.drawable.stat_sys_gps_on, 0, 0);
+        mGpsIcon = service.addIcon(mGpsEnabledIconData, null);
+        service.setIconVisibility(mGpsIcon, false);
+
+        // Alarm clock
+        mAlarmClockIconData = IconData.makeIcon(
+                "alarm_clock",
+                null, com.android.internal.R.drawable.stat_notify_alarm, 0, 0);
+        mAlarmClockIcon = service.addIcon(mAlarmClockIconData, null);
+        service.setIconVisibility(mAlarmClockIcon, false);
+
+        // Sync state
+        mSyncActiveIcon = service.addIcon(IconData.makeIcon("sync_active",
+                null, R.drawable.stat_notify_sync_anim0, 0, 0), null);
+        mSyncFailingIcon = service.addIcon(IconData.makeIcon("sync_failing",
+                null, R.drawable.stat_notify_sync_error, 0, 0), null);
+        service.setIconVisibility(mSyncActiveIcon, false);
+        service.setIconVisibility(mSyncFailingIcon, false);
+
+        // volume
+        mVolumeData = IconData.makeIcon("volume",
+                null, com.android.internal.R.drawable.stat_sys_ringer_silent, 0, 0);
+        mVolumeIcon = service.addIcon(mVolumeData, null);
+        service.setIconVisibility(mVolumeIcon, false);
+        updateVolume();
+
+        IntentFilter filter = new IntentFilter();
+
+        // Register for Intent broadcasts for...
+        filter.addAction(Intent.ACTION_TIME_TICK);
+        filter.addAction(Intent.ACTION_TIME_CHANGED);
+        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        filter.addAction(Intent.ACTION_BATTERY_LOW);
+        filter.addAction(Intent.ACTION_BATTERY_OKAY);
+        filter.addAction(Intent.ACTION_POWER_CONNECTED);
+        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+        filter.addAction(Intent.ACTION_ALARM_CHANGED);
+        filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
+        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+        filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+        filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+        filter.addAction(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+        filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+        filter.addAction(LocationManager.GPS_FIX_CHANGE_ACTION);
+        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
+        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+
+        // load config to determine if to distinguish Hspa data icon
+        try {
+            mHspaDataDistinguishable = mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_hspa_data_distinguishable);
+        } catch (Exception e) {
+            mHspaDataDistinguishable = false;
+        }
+    }
+
+    public static void installIcons(Context context, StatusBarService service) {
+        sInstance = new StatusBarPolicy(context, service);
+    }
+
+    private final CharSequence getSmallTime() {
+        boolean b24 = DateFormat.is24HourFormat(mContext);
+        int res;
+
+        if (b24) {
+            res = R.string.twenty_four_hour_time_format;
+        } else {
+            res = R.string.twelve_hour_time_format;
+        }
+
+        final char MAGIC1 = '\uEF00';
+        final char MAGIC2 = '\uEF01';
+
+        SimpleDateFormat sdf;
+        String format = mContext.getString(res);
+        if (!format.equals(mClockFormatString)) {
+            /*
+             * Search for an unquoted "a" in the format string, so we can
+             * add dummy characters around it to let us find it again after
+             * formatting and change its size.
+             */
+            int a = -1;
+            boolean quoted = false;
+            for (int i = 0; i < format.length(); i++) {
+                char c = format.charAt(i);
+
+                if (c == '\'') {
+                    quoted = !quoted;
+                }
+
+                if (!quoted && c == 'a') {
+                    a = i;
+                    break;
+                }
+            }
+
+            if (a >= 0) {
+                // Move a back so any whitespace before the AM/PM is also in the alternate size.
+                final int b = a;
+                while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
+                    a--;
+                }
+                format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
+                        + "a" + MAGIC2 + format.substring(b + 1);
+            }
+
+            mClockFormat = sdf = new SimpleDateFormat(format);
+            mClockFormatString = format;
+        } else {
+            sdf = mClockFormat;
+        }
+        String result = sdf.format(mCalendar.getTime());
+
+        int magic1 = result.indexOf(MAGIC1);
+        int magic2 = result.indexOf(MAGIC2);
+
+        if (magic1 >= 0 && magic2 > magic1) {
+            SpannableStringBuilder formatted = new SpannableStringBuilder(result);
+
+            formatted.setSpan(new RelativeSizeSpan(0.7f), magic1, magic2,
+                              Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+
+            formatted.delete(magic2, magic2 + 1);
+            formatted.delete(magic1, magic1 + 1);
+
+            return formatted;
+        } else {
+            return result;
+        }
+    }
+
+    private final void updateClock() {
+        mCalendar.setTimeInMillis(System.currentTimeMillis());
+        mClockData.text = getSmallTime();
+        mService.updateIcon(mClockIcon, mClockData, null);
+    }
+
+    private final void updateAlarm(Intent intent) {
+        boolean alarmSet = intent.getBooleanExtra("alarmSet", false);
+        mService.setIconVisibility(mAlarmClockIcon, alarmSet);
+    }
+
+    private final void updateSyncState(Intent intent) {
+        boolean isActive = intent.getBooleanExtra("active", false);
+        boolean isFailing = intent.getBooleanExtra("failing", false);
+        mService.setIconVisibility(mSyncActiveIcon, isActive);
+        // Don't display sync failing icon: BUG 1297963 Set sync error timeout to "never"
+        //mService.setIconVisibility(mSyncFailingIcon, isFailing && !isActive);
+    }
+
+    private final void updateBattery(Intent intent) {
+        mBatteryData.iconId = intent.getIntExtra("icon-small", 0);
+        mBatteryData.iconLevel = intent.getIntExtra("level", 0);
+        mService.updateIcon(mBatteryIcon, mBatteryData, null);
+
+        boolean plugged = intent.getIntExtra("plugged", 0) != 0;
+        int level = intent.getIntExtra("level", -1);
+        if (false) {
+            Slog.d(TAG, "updateBattery level=" + level
+                    + " plugged=" + plugged
+                    + " mBatteryPlugged=" + mBatteryPlugged
+                    + " mBatteryLevel=" + mBatteryLevel
+                    + " mBatteryFirst=" + mBatteryFirst);
+        }
+
+        boolean oldPlugged = mBatteryPlugged;
+
+        mBatteryPlugged = plugged;
+        mBatteryLevel = level;
+
+        if (mBatteryFirst) {
+            mBatteryFirst = false;
+        }
+        /*
+         * No longer showing the battery view because it draws attention away
+         * from the USB storage notification. We could still show it when
+         * connected to a brick, but that could lead to the user into thinking
+         * the device does not charge when plugged into USB (since he/she would
+         * not see the same battery screen on USB as he sees on brick).
+         */
+        /* else {
+            if (plugged && !oldPlugged) {
+                showBatteryView();
+            }
+        }
+        */
+        if (false) {
+            Slog.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
+        }
+    }
+
+    private void onBatteryLow(Intent intent) {
+        if (SHOW_LOW_BATTERY_WARNING) {
+            if (false) {
+                Slog.d(TAG, "mPhoneState=" + mPhoneState
+                      + " mLowBatteryDialog=" + mLowBatteryDialog
+                      + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
+            }
+
+            if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
+                showLowBatteryWarning();
+            } else {
+                mBatteryShowLowOnEndCall = true;
+            }
+        }
+    }
+
+    private void onBatteryOkay(Intent intent) {
+        if (mLowBatteryDialog != null
+                && SHOW_LOW_BATTERY_WARNING) {
+            mLowBatteryDialog.dismiss();
+            mBatteryShowLowOnEndCall = false;
+        }
+    }
+
+    private void showBatteryView() {
+        closeLastBatteryView();
+        if (mLowBatteryDialog != null) {
+            mLowBatteryDialog.dismiss();
+        }
+
+        int level = mBatteryLevel;
+
+        View v = View.inflate(mContext, com.android.internal.R.layout.battery_status, null);
+        mBatteryView = v;
+        int pixelFormat = PixelFormat.TRANSLUCENT;
+        Drawable bg = v.getBackground();
+        if (bg != null) {
+            pixelFormat = bg.getOpacity();
+        }
+
+        int flags =  WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+
+        if (!mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_sf_slowBlur)) {
+            flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
+        }
+
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                WindowManager.LayoutParams.TYPE_TOAST,
+                flags, pixelFormat);
+
+        // Get the dim amount from the theme
+        TypedArray a = mContext.obtainStyledAttributes(
+                com.android.internal.R.styleable.Theme);
+        lp.dimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
+        a.recycle();
+
+        lp.setTitle("Battery");
+
+        TextView levelTextView = (TextView)v.findViewById(com.android.internal.R.id.level_percent);
+        levelTextView.setText(mContext.getString(
+                    com.android.internal.R.string.battery_status_text_percent_format, level));
+
+        setBatteryLevel(v, com.android.internal.R.id.spacer, 100-level, 0, 0);
+        setBatteryLevel(v, com.android.internal.R.id.level, level,
+                com.android.internal.R.drawable.battery_charge_fill, level);
+
+        WindowManagerImpl.getDefault().addView(v, lp);
+
+        scheduleCloseBatteryView();
+    }
+
+    private void setBatteryLevel(View parent, int id, int height, int background, int level) {
+        ImageView v = (ImageView)parent.findViewById(id);
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)v.getLayoutParams();
+        lp.weight = height;
+        if (background != 0) {
+            v.setBackgroundResource(background);
+            Drawable bkg = v.getBackground();
+            bkg.setLevel(level);
+        }
+    }
+
+    private void showLowBatteryWarning() {
+        closeLastBatteryView();
+
+        // Show exact battery level.
+        CharSequence levelText = mContext.getString(
+                    com.android.internal.R.string.battery_low_percent_format, mBatteryLevel);
+
+        if (mBatteryLevelTextView != null) {
+            mBatteryLevelTextView.setText(levelText);
+        } else {
+            View v = View.inflate(mContext, com.android.internal.R.layout.battery_low, null);
+            mBatteryLevelTextView=(TextView)v.findViewById(com.android.internal.R.id.level_percent);
+
+            mBatteryLevelTextView.setText(levelText);
+
+            AlertDialog.Builder b = new AlertDialog.Builder(mContext);
+                b.setCancelable(true);
+                b.setTitle(com.android.internal.R.string.battery_low_title);
+                b.setView(v);
+                b.setIcon(android.R.drawable.ic_dialog_alert);
+                b.setPositiveButton(android.R.string.ok, null);
+
+                final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                        | Intent.FLAG_ACTIVITY_NO_HISTORY);
+                if (intent.resolveActivity(mContext.getPackageManager()) != null) {
+                    b.setNegativeButton(com.android.internal.R.string.battery_low_why,
+                            new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                            mContext.startActivity(intent);
+                            if (mLowBatteryDialog != null) {
+                                mLowBatteryDialog.dismiss();
+                            }
+                        }
+                    });
+                }
+
+            AlertDialog d = b.create();
+            d.setOnDismissListener(mLowBatteryListener);
+            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+            d.show();
+            mLowBatteryDialog = d;
+        }
+
+        final ContentResolver cr = mContext.getContentResolver();
+        if (Settings.System.getInt(cr,
+                Settings.System.POWER_SOUNDS_ENABLED, 1) == 1) 
+        {
+            final String soundPath = Settings.System.getString(cr,
+                Settings.System.LOW_BATTERY_SOUND);
+            if (soundPath != null) {
+                final Uri soundUri = Uri.parse("file://" + soundPath);
+                if (soundUri != null) {
+                    final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+                    if (sfx != null) {
+                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+                        sfx.play();
+                    }
+                }
+            }
+        }
+    }
+
+    private final void updateCallState(int state) {
+        mPhoneState = state;
+        if (false) {
+            Slog.d(TAG, "mPhoneState=" + mPhoneState
+                    + " mLowBatteryDialog=" + mLowBatteryDialog
+                    + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
+        }
+        if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
+            if (mBatteryShowLowOnEndCall) {
+                if (!mBatteryPlugged) {
+                    showLowBatteryWarning();
+                }
+                mBatteryShowLowOnEndCall = false;
+            }
+        } else {
+            if (mLowBatteryDialog != null) {
+                mLowBatteryDialog.dismiss();
+                mBatteryShowLowOnEndCall = true;
+            }
+        }
+    }
+
+    private DialogInterface.OnDismissListener mLowBatteryListener
+            = new DialogInterface.OnDismissListener() {
+        public void onDismiss(DialogInterface dialog) {
+            mLowBatteryDialog = null;
+            mBatteryLevelTextView = null;
+        }
+    };
+
+    private void scheduleCloseBatteryView() {
+        Message m = mHandler.obtainMessage(EVENT_BATTERY_CLOSE);
+        m.arg1 = (++mBatteryViewSequence);
+        mHandler.sendMessageDelayed(m, 3000);
+    }
+
+    private void closeLastBatteryView() {
+        if (mBatteryView != null) {
+            //mBatteryView.debug();
+            WindowManagerImpl.getDefault().removeView(mBatteryView);
+            mBatteryView = null;
+        }
+    }
+
+    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            mSignalStrength = signalStrength;
+            updateSignalStrength();
+        }
+
+        @Override
+        public void onServiceStateChanged(ServiceState state) {
+            mServiceState = state;
+            updateSignalStrength();
+            updateCdmaRoamingIcon(state);
+            updateDataIcon();
+        }
+
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            updateCallState(state);
+            // In cdma, if a voice call is made, RSSI should switch to 1x.
+            if (isCdma()) {
+                updateSignalStrength();
+            }
+        }
+
+        @Override
+        public void onDataConnectionStateChanged(int state, int networkType) {
+            mDataState = state;
+            updateDataNetType(networkType);
+            updateDataIcon();
+        }
+
+        @Override
+        public void onDataActivity(int direction) {
+            mDataActivity = direction;
+            updateDataIcon();
+        }
+    };
+
+    private final void updateSimState(Intent intent) {
+        String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
+        if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+            mSimState = IccCard.State.ABSENT;
+        }
+        else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+            mSimState = IccCard.State.READY;
+        }
+        else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+            final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
+            if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+                mSimState = IccCard.State.PIN_REQUIRED;
+            }
+            else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+                mSimState = IccCard.State.PUK_REQUIRED;
+            }
+            else {
+                mSimState = IccCard.State.NETWORK_LOCKED;
+            }
+        } else {
+            mSimState = IccCard.State.UNKNOWN;
+        }
+        updateDataIcon();
+    }
+
+    private boolean isCdma() {
+        return (mSignalStrength != null) && !mSignalStrength.isGsm();
+    }
+
+    private boolean isEvdo() {
+        return ( (mServiceState != null)
+                 && ((mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
+                     || (mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_A)));
+    }
+
+    private boolean hasService() {
+        if (mServiceState != null) {
+            switch (mServiceState.getState()) {
+                case ServiceState.STATE_OUT_OF_SERVICE:
+                case ServiceState.STATE_POWER_OFF:
+                    return false;
+                default:
+                    return true;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    private final void updateSignalStrength() {
+        int iconLevel = -1;
+        int[] iconList;
+
+        if (!hasService()) {
+            //Slog.d(TAG, "updateSignalStrength: no service");
+            if (Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
+                mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_flightmode;
+            } else {
+                mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null;
+            }
+            mService.updateIcon(mPhoneIcon, mPhoneData, null);
+            return;
+        }
+
+        if (!isCdma()) {
+            int asu = mSignalStrength.getGsmSignalStrength();
+
+            // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+            // asu = 0 (-113dB or less) is very weak
+            // signal, its better to show 0 bars to the user in such cases.
+            // asu = 99 is a special case, where the signal strength is unknown.
+            if (asu <= 2 || asu == 99) iconLevel = 0;
+            else if (asu >= 12) iconLevel = 4;
+            else if (asu >= 8)  iconLevel = 3;
+            else if (asu >= 5)  iconLevel = 2;
+            else iconLevel = 1;
+
+            // Though mPhone is a Manager, this call is not an IPC
+            if (mPhone.isNetworkRoaming()) {
+                iconList = sSignalImages_r;
+            } else {
+                iconList = sSignalImages;
+            }
+        } else {
+            iconList = this.sSignalImages;
+
+            // If 3G(EV) and 1x network are available than 3G should be
+            // displayed, displayed RSSI should be from the EV side.
+            // If a voice call is made then RSSI should switch to 1x.
+            if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
+                iconLevel = getEvdoLevel();
+                if (false) {
+                    Slog.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel());
+                }
+            } else {
+                iconLevel = getCdmaLevel();
+            }
+        }
+        mPhoneData.iconId = iconList[iconLevel];
+        mService.updateIcon(mPhoneIcon, mPhoneData, null);
+    }
+
+    private int getCdmaLevel() {
+        final int cdmaDbm = mSignalStrength.getCdmaDbm();
+        final int cdmaEcio = mSignalStrength.getCdmaEcio();
+        int levelDbm = 0;
+        int levelEcio = 0;
+
+        if (cdmaDbm >= -75) levelDbm = 4;
+        else if (cdmaDbm >= -85) levelDbm = 3;
+        else if (cdmaDbm >= -95) levelDbm = 2;
+        else if (cdmaDbm >= -100) levelDbm = 1;
+        else levelDbm = 0;
+
+        // Ec/Io are in dB*10
+        if (cdmaEcio >= -90) levelEcio = 4;
+        else if (cdmaEcio >= -110) levelEcio = 3;
+        else if (cdmaEcio >= -130) levelEcio = 2;
+        else if (cdmaEcio >= -150) levelEcio = 1;
+        else levelEcio = 0;
+
+        return (levelDbm < levelEcio) ? levelDbm : levelEcio;
+    }
+
+    private int getEvdoLevel() {
+        int evdoDbm = mSignalStrength.getEvdoDbm();
+        int evdoSnr = mSignalStrength.getEvdoSnr();
+        int levelEvdoDbm = 0;
+        int levelEvdoSnr = 0;
+
+        if (evdoDbm >= -65) levelEvdoDbm = 4;
+        else if (evdoDbm >= -75) levelEvdoDbm = 3;
+        else if (evdoDbm >= -90) levelEvdoDbm = 2;
+        else if (evdoDbm >= -105) levelEvdoDbm = 1;
+        else levelEvdoDbm = 0;
+
+        if (evdoSnr >= 7) levelEvdoSnr = 4;
+        else if (evdoSnr >= 5) levelEvdoSnr = 3;
+        else if (evdoSnr >= 3) levelEvdoSnr = 2;
+        else if (evdoSnr >= 1) levelEvdoSnr = 1;
+        else levelEvdoSnr = 0;
+
+        return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+    }
+
+    private final void updateDataNetType(int net) {
+
+        switch (net) {
+        case TelephonyManager.NETWORK_TYPE_EDGE:
+            mDataIconList = sDataNetType_e;
+            break;
+        case TelephonyManager.NETWORK_TYPE_UMTS:
+            mDataIconList = sDataNetType_3g;
+            break;
+        case TelephonyManager.NETWORK_TYPE_HSDPA:
+        case TelephonyManager.NETWORK_TYPE_HSUPA:
+        case TelephonyManager.NETWORK_TYPE_HSPA:
+            if (mHspaDataDistinguishable) {
+                mDataIconList = sDataNetType_h;
+            } else {
+                mDataIconList = sDataNetType_3g;
+            }
+            break;
+        case TelephonyManager.NETWORK_TYPE_CDMA:
+            // display 1xRTT for IS95A/B
+            mDataIconList = this.sDataNetType_1x;
+            break;
+        case TelephonyManager.NETWORK_TYPE_1xRTT:
+            mDataIconList = this.sDataNetType_1x;
+            break;
+        case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
+        case TelephonyManager.NETWORK_TYPE_EVDO_A:
+            mDataIconList = sDataNetType_3g;
+            break;
+        default:
+            mDataIconList = sDataNetType_g;
+        break;
+        }
+    }
+
+    private final void updateDataIcon() {
+        int iconId;
+        boolean visible = true;
+
+        if (!isCdma()) {
+            // GSM case, we have to check also the sim state
+            if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) {
+                if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
+                    switch (mDataActivity) {
+                        case TelephonyManager.DATA_ACTIVITY_IN:
+                            iconId = mDataIconList[1];
+                            break;
+                        case TelephonyManager.DATA_ACTIVITY_OUT:
+                            iconId = mDataIconList[2];
+                            break;
+                        case TelephonyManager.DATA_ACTIVITY_INOUT:
+                            iconId = mDataIconList[3];
+                            break;
+                        default:
+                            iconId = mDataIconList[0];
+                            break;
+                    }
+                    mDataData.iconId = iconId;
+                    mService.updateIcon(mDataIcon, mDataData, null);
+                } else {
+                    visible = false;
+                }
+            } else {
+                mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim;
+                mService.updateIcon(mDataIcon, mDataData, null);
+            }
+        } else {
+            // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
+            if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
+                switch (mDataActivity) {
+                    case TelephonyManager.DATA_ACTIVITY_IN:
+                        iconId = mDataIconList[1];
+                        break;
+                    case TelephonyManager.DATA_ACTIVITY_OUT:
+                        iconId = mDataIconList[2];
+                        break;
+                    case TelephonyManager.DATA_ACTIVITY_INOUT:
+                        iconId = mDataIconList[3];
+                        break;
+                    case TelephonyManager.DATA_ACTIVITY_DORMANT:
+                    default:
+                        iconId = mDataIconList[0];
+                        break;
+                }
+                mDataData.iconId = iconId;
+                mService.updateIcon(mDataIcon, mDataData, null);
+            } else {
+                visible = false;
+            }
+        }
+
+        long ident = Binder.clearCallingIdentity();
+        try {
+            mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible);
+        } catch (RemoteException e) {
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+
+        if (mDataIconVisible != visible) {
+            mService.setIconVisibility(mDataIcon, visible);
+            mDataIconVisible = visible;
+        }
+    }
+
+    private final void updateVolume() {
+        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        final int ringerMode = audioManager.getRingerMode();
+        final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
+                ringerMode == AudioManager.RINGER_MODE_VIBRATE;
+        final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)
+                ? com.android.internal.R.drawable.stat_sys_ringer_vibrate
+                : com.android.internal.R.drawable.stat_sys_ringer_silent;
+
+        if (visible) {
+            mVolumeData.iconId = iconId;
+            mService.updateIcon(mVolumeIcon, mVolumeData, null);
+        }
+        if (visible != mVolumeVisible) {
+            mService.setIconVisibility(mVolumeIcon, visible);
+            mVolumeVisible = visible;
+        }
+    }
+
+    private final void updateBluetooth(Intent intent) {
+        int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth;
+        String action = intent.getAction();
+        if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+            mBluetoothEnabled = state == BluetoothAdapter.STATE_ON;
+        } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
+            mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+                    BluetoothHeadset.STATE_ERROR);
+        } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
+            BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
+            if (a2dp.getConnectedSinks().size() != 0) {
+                mBluetoothA2dpConnected = true;
+            } else {
+                mBluetoothA2dpConnected = false;
+            }
+        } else if (action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
+            mBluetoothPbapState = intent.getIntExtra(BluetoothPbap.PBAP_STATE,
+                    BluetoothPbap.STATE_DISCONNECTED);
+        } else {
+            return;
+        }
+
+        if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpConnected ||
+                mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) {
+            iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected;
+        }
+
+        mBluetoothData.iconId = iconId;
+        mService.updateIcon(mBluetoothIcon, mBluetoothData, null);
+        mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
+    }
+
+    private final void updateWifi(Intent intent) {
+        final String action = intent.getAction();
+        if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+
+            final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                    WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
+
+            if (!enabled) {
+                // If disabled, hide the icon. (We show icon when connected.)
+                mService.setIconVisibility(mWifiIcon, false);
+            }
+
+        } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
+            final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED,
+                                                           false);
+            if (!enabled) {
+                mService.setIconVisibility(mWifiIcon, false);
+            }
+        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+
+            final NetworkInfo networkInfo = (NetworkInfo)
+                    intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+
+            int iconId;
+            if (networkInfo != null && networkInfo.isConnected()) {
+                mIsWifiConnected = true;
+                if (mLastWifiSignalLevel == -1) {
+                    iconId = sWifiSignalImages[0];
+                } else {
+                    iconId = sWifiSignalImages[mLastWifiSignalLevel];
+                }
+
+                // Show the icon since wi-fi is connected
+                mService.setIconVisibility(mWifiIcon, true);
+
+            } else {
+                mLastWifiSignalLevel = -1;
+                mIsWifiConnected = false;
+                iconId = sWifiSignalImages[0];
+
+                // Hide the icon since we're not connected
+                mService.setIconVisibility(mWifiIcon, false);
+            }
+
+            mWifiData.iconId = iconId;
+            mService.updateIcon(mWifiIcon, mWifiData, null);
+        } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+            final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
+            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi,
+                                                                  sWifiSignalImages.length);
+            if (newSignalLevel != mLastWifiSignalLevel) {
+                mLastWifiSignalLevel = newSignalLevel;
+                if (mIsWifiConnected) {
+                    mWifiData.iconId = sWifiSignalImages[newSignalLevel];
+                } else {
+                    mWifiData.iconId = sWifiTemporarilyNotConnectedImage;
+                }
+                mService.updateIcon(mWifiIcon, mWifiData, null);
+            }
+        }
+    }
+
+    private final void updateGps(Intent intent) {
+        final String action = intent.getAction();
+        final boolean enabled = intent.getBooleanExtra(LocationManager.EXTRA_GPS_ENABLED, false);
+
+        if (action.equals(LocationManager.GPS_FIX_CHANGE_ACTION) && enabled) {
+            // GPS is getting fixes
+            mService.updateIcon(mGpsIcon, mGpsFixIconData, null);
+            mService.setIconVisibility(mGpsIcon, true);
+        } else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
+            // GPS is off
+            mService.setIconVisibility(mGpsIcon, false);
+        } else {
+            // GPS is on, but not receiving fixes
+            mService.updateIcon(mGpsIcon, mGpsEnabledIconData, null);
+            mService.setIconVisibility(mGpsIcon, true);
+        }
+    }
+
+    private final void updateTTY(Intent intent) {
+        final String action = intent.getAction();
+        final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
+
+        if (false) Slog.v(TAG, "updateTTY: enabled: " + enabled);
+
+        if (enabled) {
+            // TTY is on
+            if (false) Slog.v(TAG, "updateTTY: set TTY on");
+            mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null);
+            mService.setIconVisibility(mTTYModeIcon, true);
+        } else {
+            // TTY is off
+            if (false) Slog.v(TAG, "updateTTY: set TTY off");
+            mService.setIconVisibility(mTTYModeIcon, false);
+        }
+    }
+
+    private final void updateCdmaRoamingIcon(ServiceState state) {
+        if (!hasService()) {
+            mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+            return;
+        }
+
+        if (!isCdma()) {
+            mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+            return;
+        }
+
+        int[] iconList = sRoamingIndicatorImages_cdma;
+        int iconIndex = state.getCdmaEriIconIndex();
+        int iconMode = state.getCdmaEriIconMode();
+
+        if (iconIndex == -1) {
+            Slog.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update");
+            return;
+        }
+
+        if (iconMode == -1) {
+            Slog.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update");
+            return;
+        }
+
+        if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) {
+            if (false) Slog.v(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon");
+            mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+            return;
+        }
+
+        switch (iconMode) {
+            case EriInfo.ROAMING_ICON_MODE_NORMAL:
+                mCdmaRoamingIndicatorIconData.iconId = iconList[iconIndex];
+                mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
+                mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
+                break;
+            case EriInfo.ROAMING_ICON_MODE_FLASH:
+                mCdmaRoamingIndicatorIconData.iconId =
+                        com.android.internal.R.drawable.stat_sys_roaming_cdma_flash;
+                mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
+                mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
+                break;
+
+        }
+        mService.updateIcon(mPhoneIcon, mPhoneData, null);
+    }
+
+
+    private class StatusBarHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case EVENT_BATTERY_CLOSE:
+                if (msg.arg1 == mBatteryViewSequence) {
+                    closeLastBatteryView();
+                }
+                break;
+            }
+        }
+    }
+}
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 de80fc0..3fe71d8 100644
--- a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarService.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarService.java
@@ -34,12 +34,22 @@
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 
+import com.android.server.status.IconData;
+import com.android.server.status.NotificationData;
+
 public abstract class StatusBarService extends Service {
     private static final String TAG = "StatusBarService";
 
     Bar mBar = new Bar();
     IStatusBarService mBarService;
 
+    /* TODO
+    H mHandler = new H();
+    Object mQueueLock = new Object();
+    ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>();
+    NotificationCallbacks mNotificationCallbacks;
+    */
+
     @Override
     public void onCreate() {
         // Put up the view
@@ -75,5 +85,28 @@
      * Implement this to add the main status bar view.
      */
     protected abstract void addStatusBarView();
+
+    public void activate() {
+    }
+
+    public void deactivate() {
+    }
+
+    public void toggle() {
+    }
+
+    public void disable(int what, IBinder token, String pkg) {
+    }
+    
+    public IBinder addIcon(IconData data, NotificationData n) {
+        return null;
+    }
+
+    public void updateIcon(IBinder key, IconData data, NotificationData n) {
+    }
+
+    public void setIconVisibility(IBinder key, boolean visible) {
+        //addPendingOp(OP_SET_VISIBLE, key, visible);
+    }
 }
 
diff --git a/services/java/com/android/server/status/StatusBarView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarView.java
similarity index 96%
rename from services/java/com/android/server/status/StatusBarView.java
rename to packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarView.java
index 5743854..ae703bf 100644
--- a/services/java/com/android/server/status/StatusBarView.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.status;
+package com.android.policy.statusbar.phone;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -27,14 +27,12 @@
 import android.view.ViewParent;
 import android.widget.FrameLayout;
 
-import com.android.internal.R;
-
 public class StatusBarView extends FrameLayout {
     private static final String TAG = "StatusBarView";
 
     static final int DIM_ANIM_TIME = 400;
     
-    StatusBarManagerService mService;
+    PhoneStatusBarService mService;
     boolean mTracking;
     int mStartX, mStartY;
     ViewGroup mNotificationIcons;
@@ -94,7 +92,7 @@
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
-        mService.updateExpandedViewPos(StatusBarManagerService.EXPANDED_LEAVE_ALONE);
+        mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
     }
 
     @Override
diff --git a/services/java/com/android/server/status/Ticker.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/Ticker.java
similarity index 98%
rename from services/java/com/android/server/status/Ticker.java
rename to packages/StatusBarPhone/src/com/android/policy/statusbar/phone/Ticker.java
index e47185b..ee82948 100644
--- a/services/java/com/android/server/status/Ticker.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/Ticker.java
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.status;
-
-import com.android.internal.R;
+package com.android.policy.statusbar.phone;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
@@ -36,7 +34,7 @@
 import java.util.ArrayList;
 
 
-abstract class Ticker {
+public abstract class Ticker {
     private static final int TICKER_SEGMENT_DELAY = 3000;
     
     private final class Segment {
diff --git a/services/java/com/android/server/status/TickerView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TickerView.java
similarity index 95%
rename from services/java/com/android/server/status/TickerView.java
rename to packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TickerView.java
index 099dffb..2b98fc5 100644
--- a/services/java/com/android/server/status/TickerView.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TickerView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.status;
+package com.android.policy.statusbar.phone;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TrackingView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TrackingView.java
new file mode 100644
index 0000000..9b813e6
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TrackingView.java
@@ -0,0 +1,63 @@
+/*
+ * 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+
+
+public class TrackingView extends LinearLayout {
+    final Display mDisplay;
+    PhoneStatusBarService mService;
+    boolean mTracking;
+    int mStartX, mStartY;
+
+    public TrackingView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mDisplay = ((WindowManager)context.getSystemService(
+                Context.WINDOW_SERVICE)).getDefaultDisplay();
+    }
+    
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        mService.updateExpandedHeight();
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+        switch (event.getKeyCode()) {
+        case KeyEvent.KEYCODE_BACK:
+            if (down) {
+                mService.deactivate();
+            }
+            return true;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mService.onTrackingViewAttached();
+    }
+}
diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java
index a24e239..e18e9dd 100644
--- a/services/java/com/android/server/status/NotificationViewList.java
+++ b/services/java/com/android/server/status/NotificationViewList.java
@@ -21,11 +21,11 @@
 import android.view.View;
 import java.util.ArrayList;
 
-class NotificationViewList {
+public class NotificationViewList {
     private ArrayList<StatusBarNotification> mOngoing = new ArrayList();
     private ArrayList<StatusBarNotification> mLatest = new ArrayList();
 
-    NotificationViewList() {
+    public NotificationViewList() {
     }
 
     private static final int indexInList(ArrayList<StatusBarNotification> list, NotificationData n){
diff --git a/services/java/com/android/server/status/StatusBarIcon.java b/services/java/com/android/server/status/StatusBarIcon.java
index dfccf94..5849ef9 100644
--- a/services/java/com/android/server/status/StatusBarIcon.java
+++ b/services/java/com/android/server/status/StatusBarIcon.java
@@ -31,7 +31,7 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-class StatusBarIcon {
+public class StatusBarIcon {
     // TODO: get this from a resource
     private static final int ICON_GAP = 8;
     private static final int ICON_WIDTH = 25;
diff --git a/services/java/com/android/server/status/StatusBarManagerService.java b/services/java/com/android/server/status/StatusBarManagerService.java
index 18cb1ed..b48a575 100644
--- a/services/java/com/android/server/status/StatusBarManagerService.java
+++ b/services/java/com/android/server/status/StatusBarManagerService.java
@@ -154,7 +154,7 @@
     
     final Context mContext;
     final Display mDisplay;
-    StatusBarView mStatusBarView;
+    View /*StatusBarView*/ mStatusBarView;
     int mPixelFormat;
     H mHandler = new H();
     Object mQueueLock = new Object();
@@ -207,8 +207,6 @@
     int mTrackingPosition; // the position of the top of the tracking view.
 
     // ticker
-    private Ticker mTicker;
-    private View mTickerView;
     private boolean mTicking;
     
     // Tracking finger for opening/closing.
@@ -240,7 +238,6 @@
         mContext = context;
         mDisplay = ((WindowManager)context.getSystemService(
                 Context.WINDOW_SERVICE)).getDefaultDisplay();
-        makeStatusBarView(context);
         mUninstallReceiver = new UninstallReceiver();
     }
 
@@ -251,106 +248,8 @@
     // ================================================================================
     // Constructing the view
     // ================================================================================
-    private void makeStatusBarView(Context context) {
-        Resources res = context.getResources();
-        mRightIconSlots = res.getStringArray(com.android.internal.R.array.status_bar_icon_order);
-        mRightIcons = new StatusBarIcon[mRightIconSlots.length];
-
-        ExpandedView expanded = (ExpandedView)View.inflate(context,
-                com.android.internal.R.layout.status_bar_expanded, null);
-        expanded.mService = this;
-        StatusBarView sb = (StatusBarView)View.inflate(context,
-                com.android.internal.R.layout.status_bar, null);
-        sb.mService = this;
-
-        // figure out which pixel-format to use for the status bar.
-        mPixelFormat = PixelFormat.TRANSLUCENT;
-        Drawable bg = sb.getBackground();
-        if (bg != null) {
-            mPixelFormat = bg.getOpacity();
-        }
-
-        mStatusBarView = sb;
-        mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
-        mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
-        mNotificationIcons.service = this;
-        mIcons = (LinearLayout)sb.findViewById(R.id.icons);
-        mTickerView = sb.findViewById(R.id.ticker);
-        mDateView = (DateView)sb.findViewById(R.id.date);
-
-        mExpandedDialog = new ExpandedDialog(context);
-        mExpandedView = expanded;
-        mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
-        mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
-        mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
-        mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
-        mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
-        mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
-        mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
-        mClearButton.setOnClickListener(mClearButtonListener);
-        mSpnLabel = (TextView)expanded.findViewById(R.id.spnLabel);
-        mPlmnLabel = (TextView)expanded.findViewById(R.id.plmnLabel);
-        mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
-        mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
-
-        mOngoingTitle.setVisibility(View.GONE);
-        mLatestTitle.setVisibility(View.GONE);
-        
-        mTicker = new MyTicker(context, sb);
-
-        TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
-        tickerView.mTicker = mTicker;
-
-        mTrackingView = (TrackingView)View.inflate(context,
-                com.android.internal.R.layout.status_bar_tracking, null);
-        mTrackingView.mService = this;
-        mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
-        mCloseView.mService = this;
-
-        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
-        // add the more icon for the notifications
-        IconData moreData = IconData.makeIcon(null, context.getPackageName(),
-                R.drawable.stat_notify_more, 0, 42);
-        mMoreIcon = new StatusBarIcon(context, moreData, mNotificationIcons);
-        mMoreIcon.view.setId(R.drawable.stat_notify_more);
-        mNotificationIcons.moreIcon = mMoreIcon;
-        mNotificationIcons.addView(mMoreIcon.view);
-
-        // set the inital view visibility
-        setAreThereNotifications();
-        mDateView.setVisibility(View.INVISIBLE);
-
-        // before we register for broadcasts
-        mPlmnLabel.setText(R.string.lockscreen_carrier_default);
-        mPlmnLabel.setVisibility(View.VISIBLE);
-        mSpnLabel.setText("");
-        mSpnLabel.setVisibility(View.GONE);
-
-        // receive broadcasts
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
-        context.registerReceiver(mBroadcastReceiver, filter);
-    }
 
     public void systemReady() {
-        final StatusBarView view = mStatusBarView;
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                view.getContext().getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.status_bar_height),
-                WindowManager.LayoutParams.TYPE_STATUS_BAR,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
-                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
-                mPixelFormat);
-        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
-        lp.setTitle("StatusBar");
-        lp.windowAnimations = R.style.Animation_StatusBar;
-
-        //WindowManagerImpl.getDefault().addView(view, lp);
     }
 
     public void systemReady2() {
@@ -431,6 +330,10 @@
     // Can be called from any thread
     // ================================================================================
     public IBinder addIcon(IconData data, NotificationData n) {
+        if (true) {
+            return new Binder();
+        }
+        // TODO: Call onto the IStatusBar
         int slot;
         // assert early-on if they using a slot that doesn't exist.
         if (data != null && n == null) {
@@ -681,6 +584,9 @@
 
     /* private */ void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
                         throws StatusBarException {
+        if (true) {
+            return;
+        }
         if (SPEW) {
             Slog.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key);
         }
@@ -715,7 +621,7 @@
                         || !CharSequences.equals(oldData.tickerText, n.tickerText))) {
                 if (0 == (mDisabled & 
                     (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
-                    mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
+                    //mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
                 }
             }
             updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
@@ -773,6 +679,9 @@
     }
 
     /* private */ void performSetIconVisibility(IBinder key, boolean visible) {
+        if (true) {
+            return;
+        }
         synchronized (mIconMap) {
             if (SPEW) {
                 Slog.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible);
@@ -1340,47 +1249,6 @@
         }
     }
 
-    private class MyTicker extends Ticker {
-        MyTicker(Context context, StatusBarView sb) {
-            super(context, sb);
-        }
-        
-        @Override
-        void tickerStarting() {
-            mTicking = true;
-            mIcons.setVisibility(View.GONE);
-            mTickerView.setVisibility(View.VISIBLE);
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
-            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
-            if (mExpandedVisible) {
-                setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
-            }
-        }
-
-        @Override
-        void tickerDone() {
-            mIcons.setVisibility(View.VISIBLE);
-            mTickerView.setVisibility(View.GONE);
-            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
-                        mTickingDoneListener));
-            if (mExpandedVisible) {
-                setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
-            }
-        }
-
-        void tickerHalting() {
-            mIcons.setVisibility(View.VISIBLE);
-            mTickerView.setVisibility(View.GONE);
-            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
-                        mTickingDoneListener));
-            if (mExpandedVisible) {
-                setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
-            }
-        }
-    }
-
     Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
         public void onAnimationEnd(Animation animation) {
             mTicking = false;
@@ -1448,7 +1316,6 @@
             pw.println("  mLatestItems: " + viewInfo(mLatestItems));
             pw.println("  mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
             pw.println("  mCloseView: " + viewInfo(mCloseView));
-            pw.println("  mTickerView: " + viewInfo(mTickerView));
             pw.println("  mScrollView: " + viewInfo(mScrollView)
                     + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
             pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
@@ -1591,12 +1458,14 @@
     }
 
     void setNotificationIconVisibility(boolean visible, int anim) {
+        /*
         int old = mNotificationIcons.getVisibility();
         int v = visible ? View.VISIBLE : View.INVISIBLE;
         if (old != v) {
             mNotificationIcons.setVisibility(v);
             mNotificationIcons.startAnimation(loadAnim(anim, null));
         }
+        */
     }
 
     void updateExpandedViewPos(int expandedPosition) {
@@ -1729,7 +1598,7 @@
                 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
                 if (mTicking) {
                     mNotificationIcons.setVisibility(View.INVISIBLE);
-                    mTicker.halt();
+                    //mTicker.halt();
                 } else {
                     setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
                 }
@@ -1741,7 +1610,7 @@
             }
         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
             if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-                mTicker.halt();
+                //mTicker.halt();
             }
         }
     }
diff --git a/services/java/com/android/server/status/StatusBarNotification.java b/services/java/com/android/server/status/StatusBarNotification.java
index e5773f7..4bb8fdb 100644
--- a/services/java/com/android/server/status/StatusBarNotification.java
+++ b/services/java/com/android/server/status/StatusBarNotification.java
@@ -19,7 +19,7 @@
 import android.os.IBinder;
 import android.view.View;
 
-class StatusBarNotification {
+public class StatusBarNotification {
     IBinder key;
     NotificationData data;
     View view;