Merge "Fix build on Linux due to different strrchr prototype."
diff --git a/Android.mk b/Android.mk
index 4e01b97..7a24663 100644
--- a/Android.mk
+++ b/Android.mk
@@ -444,7 +444,7 @@
 
 ## SDK version identifiers used in the published docs
   # major[.minor] version for current SDK. (full releases only)
-framework_docs_SDK_VERSION:=2.2
+framework_docs_SDK_VERSION:=2.3
   # release version (ie "Release x")  (full releases only)
 framework_docs_SDK_REL_ID:=1
   # flag to build offline docs for a preview release
diff --git a/api/current.xml b/api/current.xml
index 3ccb4e9..04d7a1d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -6774,6 +6774,39 @@
  visibility="public"
 >
 </field>
+<field name="overScrollFooter"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843459"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="overScrollHeader"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843458"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="overScrollMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843457"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="padding"
  type="int"
  transient="false"
@@ -12904,6 +12937,23 @@
 >
 </field>
 </class>
+<class name="R.fraction"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="R.fraction"
+ type="android.R.fraction"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+</class>
 <class name="R.id"
  extends="java.lang.Object"
  abstract="false"
@@ -18944,7 +18994,7 @@
  visibility="public"
 >
 <method name="after"
- return="void"
+ return="android.animation.AnimatorSet.Builder"
  abstract="false"
  native="false"
  synchronized="false"
@@ -18957,7 +19007,7 @@
 </parameter>
 </method>
 <method name="after"
- return="void"
+ return="android.animation.AnimatorSet.Builder"
  abstract="false"
  native="false"
  synchronized="false"
@@ -18970,7 +19020,7 @@
 </parameter>
 </method>
 <method name="before"
- return="void"
+ return="android.animation.AnimatorSet.Builder"
  abstract="false"
  native="false"
  synchronized="false"
@@ -18983,7 +19033,7 @@
 </parameter>
 </method>
 <method name="with"
- return="void"
+ return="android.animation.AnimatorSet.Builder"
  abstract="false"
  native="false"
  synchronized="false"
@@ -23927,6 +23977,21 @@
 <parameter name="packageName" type="java.lang.String">
 </parameter>
 </method>
+<method name="moveTaskToFront"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="taskId" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
 <method name="restartPackage"
  return="void"
  abstract="false"
@@ -23940,6 +24005,17 @@
 <parameter name="packageName" type="java.lang.String">
 </parameter>
 </method>
+<field name="MOVE_TASK_WITH_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="RECENT_WITH_EXCLUDED"
  type="int"
  transient="false"
@@ -32205,7 +32281,7 @@
  type="android.app.Notification"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="icon" type="int">
@@ -32254,7 +32330,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="context" type="android.content.Context">
@@ -32524,6 +32600,16 @@
  visibility="public"
 >
 </field>
+<field name="largeIcon"
+ type="android.graphics.Bitmap"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ledARGB"
  type="int"
  transient="false"
@@ -32574,27 +32660,6 @@
  visibility="public"
 >
 </field>
-<field name="tickerIcons"
- type="android.graphics.Bitmap[]"
- transient="false"
- volatile="false"
- value="null"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="tickerSubtitle"
- type="java.lang.CharSequence"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="tickerText"
  type="java.lang.CharSequence"
  transient="false"
@@ -32605,8 +32670,8 @@
  visibility="public"
 >
 </field>
-<field name="tickerTitle"
- type="java.lang.CharSequence"
+<field name="tickerView"
+ type="android.widget.RemoteViews"
  transient="false"
  volatile="false"
  static="false"
@@ -32637,6 +32702,334 @@
 >
 </field>
 </class>
+<class name="Notification.Builder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Notification.Builder"
+ type="android.app.Notification.Builder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<method name="getNotification"
+ return="android.app.Notification"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setAutoCancel"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="autoCancel" type="boolean">
+</parameter>
+</method>
+<method name="setContent"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="views" type="android.widget.RemoteViews">
+</parameter>
+</method>
+<method name="setContentInfo"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="info" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setContentIntent"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+</method>
+<method name="setContentText"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setContentTitle"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setDefaults"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="defaults" type="int">
+</parameter>
+</method>
+<method name="setDeleteIntent"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+</method>
+<method name="setFullScreenIntent"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+<parameter name="highPriority" type="boolean">
+</parameter>
+</method>
+<method name="setLargeIcon"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+</method>
+<method name="setLights"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="argb" type="int">
+</parameter>
+<parameter name="onMs" type="int">
+</parameter>
+<parameter name="offMs" type="int">
+</parameter>
+</method>
+<method name="setNumber"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="number" type="int">
+</parameter>
+</method>
+<method name="setOngoing"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ongoing" type="boolean">
+</parameter>
+</method>
+<method name="setOnlyAlertOnce"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="onlyAlertOnce" type="boolean">
+</parameter>
+</method>
+<method name="setSmallIcon"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="icon" type="int">
+</parameter>
+</method>
+<method name="setSmallIcon"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="icon" type="int">
+</parameter>
+<parameter name="level" type="int">
+</parameter>
+</method>
+<method name="setSound"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sound" type="android.net.Uri">
+</parameter>
+</method>
+<method name="setSound"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sound" type="android.net.Uri">
+</parameter>
+<parameter name="streamType" type="int">
+</parameter>
+</method>
+<method name="setTicker"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tickerText" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setTicker"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tickerText" type="java.lang.CharSequence">
+</parameter>
+<parameter name="views" type="android.widget.RemoteViews">
+</parameter>
+</method>
+<method name="setVibrate"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pattern" type="long[]">
+</parameter>
+</method>
+<method name="setWhen"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="when" type="long">
+</parameter>
+</method>
+</class>
 <class name="NotificationManager"
  extends="java.lang.Object"
  abstract="false"
@@ -32749,6 +33142,25 @@
  visibility="public"
 >
 </method>
+<method name="getActivities"
+ return="android.app.PendingIntent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="requestCode" type="int">
+</parameter>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
 <method name="getActivity"
  return="android.app.PendingIntent"
  abstract="false"
@@ -46828,6 +47240,19 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
+<method name="startActivities"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+</method>
 <method name="startActivity"
  return="void"
  abstract="true"
@@ -48285,6 +48710,19 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
+<method name="startActivities"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+</method>
 <method name="startActivity"
  return="void"
  abstract="false"
@@ -52237,6 +52675,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_ACTIVITY_CLEAR_TASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32768"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_ACTIVITY_CLEAR_TOP"
  type="int"
  transient="false"
@@ -52391,6 +52840,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_ACTIVITY_TASK_ON_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16384"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_DEBUG_LOG_RESOLUTION"
  type="int"
  transient="false"
@@ -70292,17 +70752,6 @@
 <parameter name="message" type="java.lang.String">
 </parameter>
 </constructor>
-<field name="TYPE_DRM_INFO_ACQUISITION_FAILED"
- type="int"
- transient="false"
- volatile="false"
- value="2008"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TYPE_NOT_SUPPORTED"
  type="int"
  transient="false"
@@ -70436,17 +70885,6 @@
  visibility="public"
 >
 </method>
-<field name="DRM_INFO_OBJECT"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;drm_info_object&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="DRM_INFO_STATUS_OBJECT"
  type="java.lang.String"
  transient="false"
@@ -70469,17 +70907,6 @@
  visibility="public"
 >
 </field>
-<field name="TYPE_DRM_INFO_ACQUIRED"
- type="int"
- transient="false"
- volatile="false"
- value="1003"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TYPE_DRM_INFO_PROCESSED"
  type="int"
  transient="false"
@@ -70955,6 +71382,19 @@
 </parameter>
 </constructor>
 <method name="acquireDrmInfo"
+ return="android.drm.DrmInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="drmInfoRequest" type="android.drm.DrmInfoRequest">
+</parameter>
+</method>
+<method name="acquireRights"
  return="int"
  abstract="false"
  native="false"
@@ -71152,6 +71592,32 @@
 <parameter name="mimeType" type="java.lang.String">
 </parameter>
 </method>
+<method name="getMetadata"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+</method>
+<method name="getMetadata"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
 <method name="getOriginalMimeType"
  return="java.lang.String"
  abstract="false"
@@ -98661,6 +99127,17 @@
  visibility="public"
 >
 </field>
+<field name="MODE_IN_COMMUNICATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="MODE_NORMAL"
  type="int"
  transient="false"
@@ -143821,6 +144298,17 @@
  visibility="public"
 >
 </method>
+<method name="penaltyFlashScreen"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="penaltyLog"
  return="android.os.StrictMode.ThreadPolicy.Builder"
  abstract="false"
@@ -155675,6 +156163,17 @@
  visibility="public"
 >
 </field>
+<field name="GROUP_IS_READ_ONLY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;group_is_read_only&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="GROUP_VISIBLE"
  type="java.lang.String"
  transient="false"
@@ -157507,6 +158006,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_MTP_SESSION_END"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.provider.action.MTP_SESSION_END&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_VIDEO_CAPTURE"
  type="java.lang.String"
  transient="false"
@@ -174132,6 +174642,19 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
+<method name="startActivities"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+</method>
 <method name="startActivity"
  return="void"
  abstract="false"
@@ -205203,6 +205726,17 @@
  visibility="public"
 >
 </method>
+<method name="getOverScrollMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getPaddingBottom"
  return="int"
  abstract="false"
@@ -206534,6 +207068,25 @@
 <parameter name="heightMeasureSpec" type="int">
 </parameter>
 </method>
+<method name="onOverScrolled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="scrollX" type="int">
+</parameter>
+<parameter name="scrollY" type="int">
+</parameter>
+<parameter name="clampedX" type="boolean">
+</parameter>
+<parameter name="clampedY" type="boolean">
+</parameter>
+</method>
 <method name="onRestoreInstanceState"
  return="void"
  abstract="false"
@@ -206687,6 +207240,35 @@
 <parameter name="visibility" type="int">
 </parameter>
 </method>
+<method name="overScrollBy"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="deltaX" type="int">
+</parameter>
+<parameter name="deltaY" type="int">
+</parameter>
+<parameter name="scrollX" type="int">
+</parameter>
+<parameter name="scrollY" type="int">
+</parameter>
+<parameter name="scrollRangeX" type="int">
+</parameter>
+<parameter name="scrollRangeY" type="int">
+</parameter>
+<parameter name="maxOverScrollX" type="int">
+</parameter>
+<parameter name="maxOverScrollY" type="int">
+</parameter>
+<parameter name="isTouchEvent" type="boolean">
+</parameter>
+</method>
 <method name="performClick"
  return="boolean"
  abstract="false"
@@ -207604,6 +208186,19 @@
 <parameter name="l" type="android.view.View.OnTouchListener">
 </parameter>
 </method>
+<method name="setOverScrollMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overScrollMode" type="int">
+</parameter>
+</method>
 <method name="setPadding"
  return="void"
  abstract="false"
@@ -208438,6 +209033,39 @@
  visibility="public"
 >
 </field>
+<field name="OVER_SCROLL_ALWAYS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OVER_SCROLL_IF_CONTENT_SCROLLS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OVER_SCROLL_NEVER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET"
  type="int[]"
  transient="false"
@@ -209318,6 +209946,28 @@
  visibility="public"
 >
 </method>
+<method name="getScaledOverflingDistance"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getScaledOverscrollDistance"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getScaledPagingTouchSlop"
  return="int"
  abstract="false"
@@ -220555,6 +221205,17 @@
  visibility="public"
 >
 </method>
+<method name="getShortcutInputMethodsAndSubtypes"
+ return="java.util.Map&lt;android.view.inputmethod.InputMethodInfo, java.util.List&lt;android.view.inputmethod.InputMethodSubtype&gt;&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="hideSoftInputFromInputMethod"
  return="void"
  abstract="false"
@@ -224157,6 +224818,17 @@
  visibility="public"
 >
 </method>
+<method name="getUseWebViewBackgroundForOverscrollBackground"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getUseWideViewPort"
  return="boolean"
  abstract="false"
@@ -224762,6 +225434,19 @@
 <parameter name="use" type="boolean">
 </parameter>
 </method>
+<method name="setUseWebViewBackgroundForOverscrollBackground"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="boolean">
+</parameter>
+</method>
 <method name="setUseWideViewPort"
  return="void"
  abstract="false"
@@ -237203,6 +237888,28 @@
  visibility="public"
 >
 </method>
+<method name="getOverscrollFooter"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getOverscrollHeader"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="removeFooterView"
  return="boolean"
  abstract="false"
@@ -237294,6 +238001,32 @@
 <parameter name="itemsCanFocus" type="boolean">
 </parameter>
 </method>
+<method name="setOverscrollFooter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="footer" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setOverscrollHeader"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
 <method name="setSelection"
  return="void"
  abstract="false"
@@ -237845,6 +238578,334 @@
 </parameter>
 </method>
 </interface>
+<class name="OverScroller"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="OverScroller"
+ type="android.widget.OverScroller"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="OverScroller"
+ type="android.widget.OverScroller"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="interpolator" type="android.graphics.Interpolator">
+</parameter>
+<parameter name="bounceCoefficientX" type="float">
+</parameter>
+<parameter name="bounceCoefficientY" type="float">
+</parameter>
+<parameter name="flywheel" type="boolean">
+</parameter>
+</constructor>
+<method name="abortAnimation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="computeScrollOffset"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="fling"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="velocityX" type="int">
+</parameter>
+<parameter name="velocityY" type="int">
+</parameter>
+<parameter name="minX" type="int">
+</parameter>
+<parameter name="maxX" type="int">
+</parameter>
+<parameter name="minY" type="int">
+</parameter>
+<parameter name="maxY" type="int">
+</parameter>
+</method>
+<method name="fling"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="velocityX" type="int">
+</parameter>
+<parameter name="velocityY" type="int">
+</parameter>
+<parameter name="minX" type="int">
+</parameter>
+<parameter name="maxX" type="int">
+</parameter>
+<parameter name="minY" type="int">
+</parameter>
+<parameter name="maxY" type="int">
+</parameter>
+<parameter name="overX" type="int">
+</parameter>
+<parameter name="overY" type="int">
+</parameter>
+</method>
+<method name="forceFinished"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="finished" type="boolean">
+</parameter>
+</method>
+<method name="getCurrX"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFinalX"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFinalY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStartX"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStartY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isFinished"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isOverScrolled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="notifyHorizontalEdgeReached"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="finalX" type="int">
+</parameter>
+<parameter name="overX" type="int">
+</parameter>
+</method>
+<method name="notifyVerticalEdgeReached"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="finalY" type="int">
+</parameter>
+<parameter name="overY" type="int">
+</parameter>
+</method>
+<method name="setFriction"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="friction" type="float">
+</parameter>
+</method>
+<method name="springBack"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="minX" type="int">
+</parameter>
+<parameter name="maxX" type="int">
+</parameter>
+<parameter name="minY" type="int">
+</parameter>
+<parameter name="maxY" type="int">
+</parameter>
+</method>
+<method name="startScroll"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="dx" type="int">
+</parameter>
+<parameter name="dy" type="int">
+</parameter>
+</method>
+<method name="startScroll"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="dx" type="int">
+</parameter>
+<parameter name="dy" type="int">
+</parameter>
+<parameter name="duration" type="int">
+</parameter>
+</method>
+</class>
 <class name="PopupMenu"
  extends="java.lang.Object"
  abstract="false"
@@ -248134,7 +249195,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 654d9dc..93baefd 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -7,13 +7,16 @@
 	SineSource.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright libmedia libutils libbinder libstagefright_foundation
+	libstagefright libmedia libutils libbinder libstagefright_foundation \
+        libskia
 
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
 	frameworks/base/media/libstagefright \
 	frameworks/base/media/libstagefright/include \
-	$(TOP)/frameworks/base/include/media/stagefright/openmax
+	$(TOP)/frameworks/base/include/media/stagefright/openmax \
+        external/skia/include/core \
+        external/skia/include/images \
 
 LOCAL_CFLAGS += -Wno-multichar
 
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index f0758fd..7e7f6d1 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -49,6 +49,10 @@
 #include <media/stagefright/MPEG2TSWriter.h>
 #include <media/stagefright/MPEG4Writer.h>
 
+#include <private/media/VideoFrame.h>
+#include <SkBitmap.h>
+#include <SkImageEncoder.h>
+
 #include <fcntl.h>
 
 using namespace android;
@@ -107,6 +111,20 @@
     }
 }
 
+static void displayAVCProfileLevelIfPossible(const sp<MetaData>& meta) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+        const uint8_t *ptr = (const uint8_t *)data;
+        CHECK(size >= 7);
+        CHECK(ptr[0] == 1);  // configurationVersion == 1
+        uint8_t profile = ptr[1];
+        uint8_t level = ptr[3];
+        fprintf(stderr, "AVC video profile %d and level %d\n", profile, level);
+    }
+}
+
 static void playSource(OMXClient *client, sp<MediaSource> &source) {
     sp<MetaData> meta = source->getFormat();
 
@@ -126,6 +144,7 @@
             fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime);
             return;
         }
+        displayAVCProfileLevelIfPossible(meta);
     }
 
     source.clear();
@@ -666,6 +685,19 @@
 
             if (mem != NULL) {
                 printf("captureFrame(%s) => OK\n", filename);
+
+                VideoFrame *frame = (VideoFrame *)mem->pointer();
+
+                SkBitmap bitmap;
+                bitmap.setConfig(
+                        SkBitmap::kRGB_565_Config, frame->mWidth, frame->mHeight);
+
+                bitmap.setPixels((uint8_t *)frame + sizeof(VideoFrame));
+
+                CHECK(SkImageEncoder::EncodeFile(
+                            "/sdcard/out.jpg", bitmap,
+                            SkImageEncoder::kJPEG_Type,
+                            SkImageEncoder::kDefaultQuality));
             } else {
                 mem = retriever->extractAlbumArt();
 
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 9ba9388..f5420d1 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -341,6 +341,20 @@
         return this;
     }
 
+    @Override
+    public void setupStartValues() {
+        for (Node node : mNodes) {
+            node.animation.setupStartValues();
+        }
+    }
+
+    @Override
+    public void setupEndValues() {
+        for (Node node : mNodes) {
+            node.animation.setupEndValues();
+        }
+    }
+
     /**
      * {@inheritDoc}
      *
@@ -401,6 +415,7 @@
                     }
                 }
             });
+            delayAnim.start();
         }
         if (mListeners != null) {
             ArrayList<AnimatorListener> tmpListeners =
@@ -408,6 +423,11 @@
             int numListeners = tmpListeners.size();
             for (int i = 0; i < numListeners; ++i) {
                 tmpListeners.get(i).onAnimationStart(this);
+                if (mNodes.size() == 0) {
+                    // Handle unusual case where empty AnimatorSet is started - should send out
+                    // end event immediately since the event will not be sent out at all otherwise
+                    tmpListeners.get(i).onAnimationEnd(this);
+                }
             }
         }
     }
@@ -894,7 +914,7 @@
          * @param anim The animation that will play when the animation supplied to the
          * {@link AnimatorSet#play(Animator)} method starts.
          */
-        public void with(Animator anim) {
+        public Builder with(Animator anim) {
             Node node = mNodeMap.get(anim);
             if (node == null) {
                 node = new Node(anim);
@@ -903,6 +923,7 @@
             }
             Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
             node.addDependency(dependency);
+            return this;
         }
 
         /**
@@ -913,7 +934,7 @@
          * @param anim The animation that will play when the animation supplied to the
          * {@link AnimatorSet#play(Animator)} method ends.
          */
-        public void before(Animator anim) {
+        public Builder before(Animator anim) {
             Node node = mNodeMap.get(anim);
             if (node == null) {
                 node = new Node(anim);
@@ -922,6 +943,7 @@
             }
             Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
             node.addDependency(dependency);
+            return this;
         }
 
         /**
@@ -932,7 +954,7 @@
          * @param anim The animation whose end will cause the animation supplied to the
          * {@link AnimatorSet#play(Animator)} method to play.
          */
-        public void after(Animator anim) {
+        public Builder after(Animator anim) {
             Node node = mNodeMap.get(anim);
             if (node == null) {
                 node = new Node(anim);
@@ -941,6 +963,7 @@
             }
             Dependency dependency = new Dependency(node, Dependency.AFTER);
             mCurrentNode.addDependency(dependency);
+            return this;
         }
 
         /**
@@ -951,11 +974,12 @@
          * @param delay The number of milliseconds that should elapse before the
          * animation starts.
          */
-        public void after(long delay) {
+        public Builder after(long delay) {
             // setup dummy ValueAnimator just to run the clock
             ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
             anim.setDuration(delay);
             after(anim);
+            return this;
         }
 
     }
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index b021e75..e192067 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -19,6 +19,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.util.AndroidRuntimeException;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AnimationUtils;
 
@@ -860,21 +861,22 @@
     /**
      * Start the animation playing. This version of start() takes a boolean flag that indicates
      * whether the animation should play in reverse. The flag is usually false, but may be set
-     * to true if called from the reverse() method/
+     * to true if called from the reverse() method.
+     *
+     * <p>The animation started by calling this method will be run on the thread that called
+     * this method. This thread should have a Looper on it (a runtime exception will be thrown if
+     * this is not the case). Also, if the animation will animate
+     * properties of objects in the view hierarchy, then the calling thread should be the UI
+     * thread for that view hierarchy.</p>
      *
      * @param playBackwards Whether the ValueAnimator should start playing in reverse.
      */
     private void start(boolean playBackwards) {
-        mPlayingBackwards = playBackwards;
-        Looper looper = Looper.getMainLooper();
-        final boolean isUiThread;
-        if (looper != null) {
-            isUiThread = Thread.currentThread() == looper.getThread();
-        } else {
-            // ignore check if we don't have a Looper (this isn't an Activity)
-            isUiThread = true;
+        if (Looper.myLooper() == null) {
+            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
         }
-        if ((mStartDelay == 0) && isUiThread) {
+        mPlayingBackwards = playBackwards;
+        if (mStartDelay == 0) {
             if (mListeners != null) {
                 ArrayList<AnimatorListener> tmpListeners =
                         (ArrayList<AnimatorListener>) mListeners.clone();
@@ -912,9 +914,14 @@
                 listener.onAnimationCancel(this);
             }
         }
-        // Just set the CANCELED flag - this causes the animation to end the next time a frame
-        // is processed.
-        mPlayingState = CANCELED;
+        // Only cancel if the animation is actually running or has been started and is about
+        // to run
+        if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) ||
+                sDelayedAnims.get().contains(this)) {
+            // Just set the CANCELED flag - this causes the animation to end the next time a frame
+            // is processed.
+            mPlayingState = CANCELED;
+        }
     }
 
     @Override
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5174f19..d69a179 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3135,6 +3135,30 @@
     }
 
     /**
+     * Launch a new activity.  You will not receive any information about when
+     * the activity exits.  This implementation overrides the base version,
+     * providing information about
+     * the activity performing the launch.  Because of this additional
+     * information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not
+     * required; if not specified, the new activity will be added to the
+     * task of the caller.
+     *
+     * <p>This method throws {@link android.content.ActivityNotFoundException}
+     * if there was no Activity found to run the given Intent.
+     *
+     * @param intents The intents to start.
+     *
+     * @throws android.content.ActivityNotFoundException
+     *
+     * @see #startActivityForResult
+     */
+    @Override
+    public void startActivities(Intent[] intents) {
+        mInstrumentation.execStartActivities(this, mMainThread.getApplicationThread(),
+                mToken, this, intents);
+    }
+
+    /**
      * Like {@link #startActivity(Intent)}, but taking a IntentSender
      * to start; see
      * {@link #startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}
@@ -3616,7 +3640,7 @@
                 ActivityManagerNative.getDefault().getIntentSender(
                         IActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
                         mParent == null ? mToken : mParent.mToken,
-                        mEmbeddedID, requestCode, data, null, flags);
+                        mEmbeddedID, requestCode, new Intent[] { data }, null, flags);
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
             // Empty
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index fe1e7d7..e168034 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -334,6 +334,32 @@
     }
 
     /**
+     * Flag for {@link #moveTaskToFront(int, int)}: also move the "home"
+     * activity along with the task, so it is positioned immediately behind
+     * the task.
+     */
+    public static final int MOVE_TASK_WITH_HOME = 0x00000001;
+
+    /**
+     * Ask that the task associated with a given task ID be moved to the
+     * front of the stack, so it is now visible to the user.  Requires that
+     * the caller hold permission {@link android.Manifest.permission#REORDER_TASKS}
+     * or a SecurityException will be thrown.
+     *
+     * @param taskId The identifier of the task to be moved, as found in
+     * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
+     * @param flags Additional operational flags, 0 or more of
+     * {@link #MOVE_TASK_WITH_HOME}.
+     */
+    public void moveTaskToFront(int taskId, int flags) {
+        try {
+            ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags);
+        } catch (RemoteException e) {
+            // System dead, we will be dead too soon!
+        }
+    }
+
+    /**
      * Information you can retrieve about a particular Service that is
      * currently running in the system.
      */
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 8cc6428..273e3c6 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -492,7 +492,8 @@
         case MOVE_TASK_TO_FRONT_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int task = data.readInt();
-            moveTaskToFront(task);
+            int fl = data.readInt();
+            moveTaskToFront(task, fl);
             reply.writeNoException();
             return true;
         }
@@ -791,13 +792,19 @@
             IBinder token = data.readStrongBinder();
             String resultWho = data.readString();
             int requestCode = data.readInt();
-            Intent requestIntent = data.readInt() != 0
-                    ? Intent.CREATOR.createFromParcel(data) : null;
-            String requestResolvedType = data.readString();
+            Intent[] requestIntents;
+            String[] requestResolvedTypes;
+            if (data.readInt() != 0) {
+                requestIntents = data.createTypedArray(Intent.CREATOR);
+                requestResolvedTypes = data.createStringArray();
+            } else {
+                requestIntents = null;
+                requestResolvedTypes = null;
+            }
             int fl = data.readInt();
             IIntentSender res = getIntentSender(type, packageName, token,
-                    resultWho, requestCode, requestIntent,
-                    requestResolvedType, fl);
+                    resultWho, requestCode, requestIntents,
+                    requestResolvedTypes, fl);
             reply.writeNoException();
             reply.writeStrongBinder(res != null ? res.asBinder() : null);
             return true;
@@ -1355,6 +1362,33 @@
             return true;
         }
 
+        case START_ACTIVITIES_IN_PACKAGE_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            int uid = data.readInt();
+            Intent[] intents = data.createTypedArray(Intent.CREATOR);
+            String[] resolvedTypes = data.createStringArray();
+            IBinder resultTo = data.readStrongBinder();
+            int result = startActivitiesInPackage(uid, intents, resolvedTypes, resultTo);
+            reply.writeNoException();
+            reply.writeInt(result);
+            return true;
+        }
+
+        case START_ACTIVITIES_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder b = data.readStrongBinder();
+            IApplicationThread app = ApplicationThreadNative.asInterface(b);
+            Intent[] intents = data.createTypedArray(Intent.CREATOR);
+            String[] resolvedTypes = data.createStringArray();
+            IBinder resultTo = data.readStrongBinder();
+            int result = startActivities(app, intents, resolvedTypes, resultTo);
+            reply.writeNoException();
+            reply.writeInt(result);
+            return true;
+        }
+
         }
         
         return super.onTransact(code, data, reply, flags);
@@ -1829,12 +1863,13 @@
         reply.recycle();
         return list;
     }
-    public void moveTaskToFront(int task) throws RemoteException
+    public void moveTaskToFront(int task, int flags) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(task);
+        data.writeInt(flags);
         mRemote.transact(MOVE_TASK_TO_FRONT_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -2283,7 +2318,7 @@
     }
     public IIntentSender getIntentSender(int type,
             String packageName, IBinder token, String resultWho,
-            int requestCode, Intent intent, String resolvedType, int flags)
+            int requestCode, Intent[] intents, String[] resolvedTypes, int flags)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -2293,13 +2328,13 @@
         data.writeStrongBinder(token);
         data.writeString(resultWho);
         data.writeInt(requestCode);
-        if (intent != null) {
+        if (intents != null) {
             data.writeInt(1);
-            intent.writeToParcel(data, 0);
+            data.writeTypedArray(intents, 0);
+            data.writeStringArray(resolvedTypes);
         } else {
             data.writeInt(0);
         }
-        data.writeString(resolvedType);
         data.writeInt(flags);
         mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
         reply.readException();
@@ -3026,5 +3061,39 @@
         return res;
     }
     
+    public int startActivities(IApplicationThread caller,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+        data.writeTypedArray(intents, 0);
+        data.writeStringArray(resolvedTypes);
+        data.writeStrongBinder(resultTo);
+        mRemote.transact(START_ACTIVITIES_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int result = reply.readInt();
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
+
+    public int startActivitiesInPackage(int uid,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(uid);
+        data.writeTypedArray(intents, 0);
+        data.writeStringArray(resolvedTypes);
+        data.writeStrongBinder(resultTo);
+        mRemote.transact(START_ACTIVITIES_IN_PACKAGE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int result = reply.readInt();
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c0714e3..5c4f57a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -870,6 +870,12 @@
                             (dbStats.dbSize > 0) ? String.valueOf(dbStats.dbSize) : " ",
                             (dbStats.lookaside > 0) ? String.valueOf(dbStats.lookaside) : " ",
                             dbStats.cache, dbStats.dbName);
+                    if (dbStats.dataDump != null) {
+                        int size = dbStats.dataDump.size();
+                        for (int dumpIndex = 0; dumpIndex < size; dumpIndex++) {
+                            printRow(pw, "%s", dbStats.dataDump.get(dumpIndex));
+                        }
+                    }
                 }
             }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 82b3f75..d2de382 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -844,6 +844,19 @@
     }
 
     @Override
+    public void startActivities(Intent[] intents) {
+        if ((intents[0].getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+            throw new AndroidRuntimeException(
+                    "Calling startActivities() from outside of an Activity "
+                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag on first Intent."
+                    + " Is this really what you want?");
+        }
+        mMainThread.getInstrumentation().execStartActivities(
+            getOuterContext(), mMainThread.getApplicationThread(), null,
+            (Activity)null, intents);
+    }
+
+    @Override
     public void startIntentSender(IntentSender intent,
             Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
             throws IntentSender.SendIntentException {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c9d5448..dc18083 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -134,7 +134,7 @@
     public List getServices(int maxNum, int flags) throws RemoteException;
     public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
             throws RemoteException;
-    public void moveTaskToFront(int task) throws RemoteException;
+    public void moveTaskToFront(int task, int flags) throws RemoteException;
     public void moveTaskToBack(int task) throws RemoteException;
     public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
     public void moveTaskBackwards(int task) throws RemoteException;
@@ -199,7 +199,8 @@
     public static final int INTENT_SENDER_SERVICE = 4;
     public IIntentSender getIntentSender(int type,
             String packageName, IBinder token, String resultWho,
-            int requestCode, Intent intent, String resolvedType, int flags) throws RemoteException;
+            int requestCode, Intent[] intents, String[] resolvedTypes,
+            int flags) throws RemoteException;
     public void cancelIntentSender(IIntentSender sender) throws RemoteException;
     public boolean clearApplicationUserData(final String packageName,
             final IPackageDataObserver observer) throws RemoteException;
@@ -208,7 +209,8 @@
     public void setProcessLimit(int max) throws RemoteException;
     public int getProcessLimit() throws RemoteException;
     
-    public void setProcessForeground(IBinder token, int pid, boolean isForeground) throws RemoteException;
+    public void setProcessForeground(IBinder token, int pid,
+            boolean isForeground) throws RemoteException;
     
     public int checkPermission(String permission, int pid, int uid)
             throws RemoteException;
@@ -332,6 +334,11 @@
     public boolean dumpHeap(String process, boolean managed, String path,
         ParcelFileDescriptor fd) throws RemoteException;
 
+    public int startActivities(IApplicationThread caller,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException;
+    public int startActivitiesInPackage(int uid,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -544,4 +551,6 @@
     int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
     int CHECK_GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118;
     int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+119;
+    int START_ACTIVITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+120;
+    int START_ACTIVITIES_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+121;
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 1deb9fb..ad811d8 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1354,8 +1354,8 @@
      * {@hide}
      */
     public ActivityResult execStartActivity(
-        Context who, IBinder contextThread, IBinder token, Activity target,
-        Intent intent, int requestCode) {
+            Context who, IBinder contextThread, IBinder token, Activity target,
+            Intent intent, int requestCode) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
         if (mActivityMonitors != null) {
             synchronized (mSync) {
@@ -1386,6 +1386,44 @@
 
     /**
      * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
+     * but accepts an array of activities to be started.  Note that active
+     * {@link ActivityMonitor} objects only match against the first activity in
+     * the array.
+     *
+     * {@hide}
+     */
+    public void execStartActivities(Context who, IBinder contextThread,
+            IBinder token, Activity target, Intent[] intents) {
+        IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (mActivityMonitors != null) {
+            synchronized (mSync) {
+                final int N = mActivityMonitors.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityMonitor am = mActivityMonitors.get(i);
+                    if (am.match(who, null, intents[0])) {
+                        am.mHits++;
+                        if (am.isBlocking()) {
+                            return;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        try {
+            String[] resolvedTypes = new String[intents.length];
+            for (int i=0; i<intents.length; i++) {
+                resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
+            }
+            int result = ActivityManagerNative.getDefault()
+                .startActivities(whoThread, intents, resolvedTypes, token);
+            checkStartActivityResult(result, intents[0]);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
      * but for calls from a {#link Fragment}.
      * 
      * @param who The Context from which the activity is being started.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e602518..f5a46c5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import java.text.NumberFormat;
 import java.util.Date;
 
 import android.app.PendingIntent;
@@ -29,8 +30,12 @@
 import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.View;
 import android.widget.RemoteViews;
 
+import com.android.internal.R;
+
 /**
  * A class that represents how a persistent notification is to be presented to
  * the user using the {@link android.app.NotificationManager}.
@@ -41,6 +46,8 @@
  */
 public class Notification implements Parcelable
 {
+    private static final String TAG = "Notification";
+
     /**
      * Use all default values (where applicable).
      */
@@ -84,6 +91,15 @@
     public int icon;
 
     /**
+     * If the icon in the status bar is to have more than one level, you can set this.  Otherwise,
+     * leave it at its default value of 0.
+     *
+     * @see android.widget.ImageView#setImageLevel
+     * @see android.graphics.drawable#setLevel
+     */
+    public int iconLevel;
+
+    /**
      * The number of events that this notification represents.  For example, in a new mail
      * notification, this could be the number of unread messages.  This number is superimposed over
      * the icon in the status bar.  If the number is 0 or negative, it is not shown in the status
@@ -132,38 +148,15 @@
      * text for when the text scrolls in and when it is displayed all at once
      * in conjunction with one or more icons.
      *
-     * @see #tickerTitle
-     * @see #tickerSubtitle
-     * @see #tickerIcons
+     * @see #tickerView
      */
     public CharSequence tickerText;
 
     /**
-     * The title line for the ticker over a the fat status bar on xlarge devices.
-     *
-     * @see #tickerText
-     * @see #tickerSubtitle
-     * @see #tickerIcons
+     * The view to show as the ticker in the status bar when the notification
+     * is posted.
      */
-    public CharSequence tickerTitle;
-
-    /**
-     * The subtitle line for the ticker over a the fat status bar on xlarge devices.
-     *
-     * @see #tickerText
-     * @see #tickerTitle
-     * @see #tickerIcons
-     */
-    public CharSequence tickerSubtitle;
-
-    /**
-     * The icons to show to the left of the other ticker fields.
-     *
-     * @see #tickerText
-     * @see #tickerTitle
-     * @see #tickerSubtitle
-     */
-    public Bitmap[] tickerIcons;
+    public RemoteViews tickerView;
 
     /**
      * The view that will represent this notification in the expanded status bar.
@@ -171,13 +164,9 @@
     public RemoteViews contentView;
 
     /**
-     * If the icon in the status bar is to have more than one level, you can set this.  Otherwise,
-     * leave it at its default value of 0.
-     *
-     * @see android.widget.ImageView#setImageLevel
-     * @see android.graphics.drawable#setLevel
+     * The bitmap that may escape the bounds of the panel and bar.
      */
-    public int iconLevel;
+    public Bitmap largeIcon;
 
     /**
      * The sound to play.
@@ -327,6 +316,7 @@
 
     /**
      * Constructs a Notification object with everything set to 0.
+     * You might want to consider using {@link Builder} instead.
      */
     public Notification()
     {
@@ -334,7 +324,6 @@
     }
 
     /**
-     * @deprecated use {@link #Notification(int,CharSequence,long)} and {@link #setLatestEventInfo}.
      * @hide
      */
     public Notification(Context context, int icon, CharSequence tickerText, long when,
@@ -356,7 +345,10 @@
      *                      activates.
      * @param when          The time to show in the time field.  In the System.currentTimeMillis
      *                      timebase.
+     *
+     * @deprecated Use {@link Builder} instead.
      */
+    @Deprecated
     public Notification(int icon, CharSequence tickerText, long when)
     {
         this.icon = icon;
@@ -384,23 +376,14 @@
             tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
         }
         if (parcel.readInt() != 0) {
-            tickerTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
-        }
-        if (parcel.readInt() != 0) {
-            tickerSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
-        }
-        final int tickerIconCount = parcel.readInt();
-        if (tickerIconCount >= 0) {
-            tickerIcons = new Bitmap[tickerIconCount];
-            for (int i=0; i<tickerIconCount; i++) {
-                if (parcel.readInt() != 0) {
-                    tickerIcons[i] = Bitmap.CREATOR.createFromParcel(parcel);
-                }
-            }
+            tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
         }
         if (parcel.readInt() != 0) {
             contentView = RemoteViews.CREATOR.createFromParcel(parcel);
         }
+        if (parcel.readInt() != 0) {
+            largeIcon = Bitmap.CREATOR.createFromParcel(parcel);
+        }
         defaults = parcel.readInt();
         flags = parcel.readInt();
         if (parcel.readInt() != 0) {
@@ -434,22 +417,15 @@
         if (this.tickerText != null) {
             that.tickerText = this.tickerText.toString();
         }
-        if (this.tickerTitle != null) {
-            that.tickerTitle = this.tickerTitle.toString();
-        }
-        if (this.tickerSubtitle != null) {
-            that.tickerSubtitle = this.tickerSubtitle.toString();
-        }
-        if (this.tickerIcons != null) {
-            final int N = this.tickerIcons.length;
-            that.tickerIcons = new Bitmap[N];
-            for (int i=0; i<N; i++) {
-                that.tickerIcons[i] = Bitmap.createBitmap(this.tickerIcons[i]);
-            }
+        if (this.tickerView != null) {
+            that.tickerView = this.tickerView.clone();
         }
         if (this.contentView != null) {
             that.contentView = this.contentView.clone();
         }
+        if (this.largeIcon != null) {
+            that.largeIcon = Bitmap.createBitmap(this.largeIcon);
+        }
         that.iconLevel = that.iconLevel;
         that.sound = this.sound; // android.net.Uri is immutable
         that.audioStreamType = this.audioStreamType;
@@ -503,38 +479,24 @@
         } else {
             parcel.writeInt(0);
         }
-        if (tickerTitle != null) {
+        if (tickerView != null) {
             parcel.writeInt(1);
-            TextUtils.writeToParcel(tickerTitle, parcel, flags);
+            tickerView.writeToParcel(parcel, 0);
         } else {
             parcel.writeInt(0);
         }
-        if (tickerSubtitle != null) {
-            parcel.writeInt(1);
-            TextUtils.writeToParcel(tickerSubtitle, parcel, flags);
-        } else {
-            parcel.writeInt(0);
-        }
-        if (tickerIcons != null) {
-            final int N = tickerIcons.length;
-            parcel.writeInt(N);
-            for (int i=0; i<N; i++) {
-                if (tickerIcons[i] != null) {
-                    parcel.writeInt(1);
-                    tickerIcons[i].writeToParcel(parcel, flags);
-                } else {
-                    parcel.writeInt(0);
-                }
-            }
-        } else {
-            parcel.writeInt(-1);
-        }
         if (contentView != null) {
             parcel.writeInt(1);
             contentView.writeToParcel(parcel, 0);
         } else {
             parcel.writeInt(0);
         }
+        if (largeIcon != null) {
+            parcel.writeInt(1);
+            largeIcon.writeToParcel(parcel, 0);
+        } else {
+            parcel.writeInt(0);
+        }
 
         parcel.writeInt(defaults);
         parcel.writeInt(this.flags);
@@ -591,22 +553,25 @@
      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
      * that you take care of task management as described in 
      * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application Fundamentals: Activities and Tasks</a>.
+     * 
+     * @deprecated Use {@link Builder} instead.
      */
+    @Deprecated
     public void setLatestEventInfo(Context context,
             CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
         RemoteViews contentView = new RemoteViews(context.getPackageName(),
-                com.android.internal.R.layout.status_bar_latest_event_content);
+                R.layout.status_bar_latest_event_content);
         if (this.icon != 0) {
-            contentView.setImageViewResource(com.android.internal.R.id.icon, this.icon);
+            contentView.setImageViewResource(R.id.icon, this.icon);
         }
         if (contentTitle != null) {
-            contentView.setTextViewText(com.android.internal.R.id.title, contentTitle);
+            contentView.setTextViewText(R.id.title, contentTitle);
         }
         if (contentText != null) {
-            contentView.setTextViewText(com.android.internal.R.id.text, contentText);
+            contentView.setTextViewText(R.id.text, contentText);
         }
         if (this.when != 0) {
-            contentView.setLong(com.android.internal.R.id.time, "setTime", when);
+            contentView.setLong(R.id.time, "setTime", when);
         }
 
         this.contentView = contentView;
@@ -651,4 +616,243 @@
         sb.append(")");
         return sb.toString();
     }
+
+    public static class Builder {
+        private Context mContext;
+
+        private long mWhen;
+        private int mSmallIcon;
+        private int mSmallIconLevel;
+        private int mNumber;
+        private CharSequence mContentTitle;
+        private CharSequence mContentText;
+        private CharSequence mContentInfo;
+        private PendingIntent mContentIntent;
+        private RemoteViews mContentView;
+        private PendingIntent mDeleteIntent;
+        private PendingIntent mFullScreenIntent;
+        private CharSequence mTickerText;
+        private RemoteViews mTickerView;
+        private Bitmap mLargeIcon;
+        private Uri mSound;
+        private int mAudioStreamType;
+        private long[] mVibrate;
+        private int mLedArgb;
+        private int mLedOnMs;
+        private int mLedOffMs;
+        private int mDefaults;
+        private int mFlags;
+
+        public Builder(Context context) {
+            mContext = context;
+            mWhen = System.currentTimeMillis();
+        }
+
+        public Builder setWhen(long when) {
+            mWhen = when;
+            return this;
+        }
+
+        public Builder setSmallIcon(int icon) {
+            mSmallIcon = icon;
+            return this;
+        }
+
+        public Builder setSmallIcon(int icon, int level) {
+            mSmallIcon = icon;
+            mSmallIconLevel = level;
+            return this;
+        }
+
+        public Builder setContentTitle(CharSequence title) {
+            mContentTitle = title;
+            return this;
+        }
+
+        public Builder setContentText(CharSequence text) {
+            mContentText = text;
+            return this;
+        }
+
+        public Builder setNumber(int number) {
+            mNumber = number;
+            return this;
+        }
+
+        public Builder setContentInfo(CharSequence info) {
+            mContentInfo = info;
+            return this;
+        }
+
+        public Builder setContent(RemoteViews views) {
+            mContentView = views;
+            return this;
+        }
+
+        public Builder setContentIntent(PendingIntent intent) {
+            mContentIntent = intent;
+            return this;
+        }
+
+        public Builder setDeleteIntent(PendingIntent intent) {
+            mDeleteIntent = intent;
+            return this;
+        }
+
+        public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
+            mFullScreenIntent = intent;
+            setFlag(FLAG_HIGH_PRIORITY, highPriority);
+            return this;
+        }
+
+        public Builder setTicker(CharSequence tickerText) {
+            mTickerText = tickerText;
+            return this;
+        }
+
+        public Builder setTicker(CharSequence tickerText, RemoteViews views) {
+            mTickerText = tickerText;
+            mTickerView = views;
+            return this;
+        }
+
+        public Builder setLargeIcon(Bitmap icon) {
+            mLargeIcon = icon;
+            return this;
+        }
+
+        public Builder setSound(Uri sound) {
+            mSound = sound;
+            mAudioStreamType = STREAM_DEFAULT;
+            return this;
+        }
+
+        public Builder setSound(Uri sound, int streamType) {
+            mSound = sound;
+            mAudioStreamType = streamType;
+            return this;
+        }
+
+        public Builder setVibrate(long[] pattern) {
+            mVibrate = pattern;
+            return this;
+        }
+
+        public Builder setLights(int argb, int onMs, int offMs) {
+            mLedArgb = argb;
+            mLedOnMs = onMs;
+            mLedOffMs = offMs;
+            return this;
+        }
+
+        public Builder setOngoing(boolean ongoing) {
+            setFlag(FLAG_ONGOING_EVENT, ongoing);
+            return this;
+        }
+
+        public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
+            setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
+            return this;
+        }
+
+        public Builder setAutoCancel(boolean autoCancel) {
+            setFlag(FLAG_ONLY_ALERT_ONCE, autoCancel);
+            return this;
+        }
+
+        public Builder setDefaults(int defaults) {
+            mDefaults = defaults;
+            return this;
+        }
+
+        private void setFlag(int mask, boolean value) {
+            if (value) {
+                mFlags |= mask;
+            } else {
+                mFlags &= ~mask;
+            }
+        }
+
+        private RemoteViews makeRemoteViews(int resId) {
+            RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
+            if (mSmallIcon != 0) {
+                contentView.setImageViewResource(R.id.icon, mSmallIcon);
+            }
+            if (mContentTitle != null) {
+                contentView.setTextViewText(R.id.title, mContentTitle);
+            }
+            if (mContentText != null) {
+                contentView.setTextViewText(R.id.text, mContentText);
+            }
+            if (mContentInfo != null) {
+                contentView.setTextViewText(R.id.info, mContentInfo);
+            } else if (mNumber > 0) {
+                NumberFormat f = NumberFormat.getIntegerInstance();
+                contentView.setTextViewText(R.id.info, f.format(mNumber));
+                contentView.setFloat(R.id.info, "setTextSize",
+                        mContext.getResources().getDimensionPixelSize(
+                            R.dimen.status_bar_content_number_size));
+            } else {
+                contentView.setViewVisibility(R.id.info, View.GONE);
+            }
+            if (mWhen != 0) {
+                contentView.setLong(R.id.time, "setTime", mWhen);
+            }
+            return contentView;
+        }
+
+        private RemoteViews makeContentView() {
+            if (mContentView != null) {
+                return mContentView;
+            } else {
+                    return makeRemoteViews(mLargeIcon == null
+                            ? R.layout.status_bar_latest_event_content
+                        : R.layout.status_bar_latest_event_content_large_icon);
+            }
+        }
+
+        private RemoteViews makeTickerView() {
+            if (mTickerView != null) {
+                return mTickerView;
+            } else {
+                if (mContentView == null) {
+                    return makeRemoteViews(mLargeIcon == null
+                            ? R.layout.status_bar_latest_event_ticker
+                            : R.layout.status_bar_latest_event_ticker_large_icon);
+                } else {
+                    return null;
+                }
+            }
+        }
+
+        public Notification getNotification() {
+            Notification n = new Notification();
+            n.when = mWhen;
+            n.icon = mSmallIcon;
+            n.iconLevel = mSmallIconLevel;
+            n.number = mNumber;
+            n.contentView = makeContentView();
+            n.contentIntent = mContentIntent;
+            n.deleteIntent = mDeleteIntent;
+            n.fullScreenIntent = mFullScreenIntent;
+            n.tickerText = mTickerText;
+            n.tickerView = makeTickerView();
+            n.largeIcon = mLargeIcon;
+            n.sound = mSound;
+            n.audioStreamType = mAudioStreamType;
+            n.vibrate = mVibrate;
+            n.ledARGB = mLedArgb;
+            n.ledOnMS = mLedOnMs;
+            n.ledOffMS = mLedOffMs;
+            n.defaults = mDefaults;
+            n.flags = mFlags;
+            if (mLedOnMs != 0 && mLedOffMs != 0) {
+                n.flags |= FLAG_SHOW_LIGHTS;
+            }
+            if ((mDefaults & DEFAULT_LIGHTS) != 0) {
+                n.flags |= FLAG_SHOW_LIGHTS;
+            }
+            return n;
+        }
+    }
 }
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index be1dc4a..5b43b65 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -195,7 +195,67 @@
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     IActivityManager.INTENT_SENDER_ACTIVITY, packageName,
-                    null, null, requestCode, intent, resolvedType, flags);
+                    null, null, requestCode, new Intent[] { intent },
+                    resolvedType != null ? new String[] { resolvedType } : null, flags);
+            return target != null ? new PendingIntent(target) : null;
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
+    /**
+     * Like {@link #getActivity(Context, int, Intent, int)}, but allows an
+     * array of Intents to be supplied.  The first Intent in the array is
+     * taken as the primary key for the PendingIntent, like the single Intent
+     * given to {@link #getActivity(Context, int, Intent, int)}.  Upon sending
+     * the resulting PendingIntent, all of the Intents are started in the same
+     * way as they would be by passing them to {@link Context#startActivities(Intent[])}.
+     *
+     * <p class="note">
+     * The <em>first</em> intent in the array will be started outside of the context of an
+     * existing activity, so you must use the {@link Intent#FLAG_ACTIVITY_NEW_TASK
+     * Intent.FLAG_ACTIVITY_NEW_TASK} launch flag in the Intent.  (Activities after
+     * the first in the array are started in the context of the previous activity
+     * in the array, so FLAG_ACTIVITY_NEW_TASK is not needed nor desired for them.)
+     * </p>
+     *
+     * <p class="note">
+     * The <em>last</em> intent in the array represents the key for the
+     * PendingIntent.  In other words, it is the significant element for matching
+     * (as done with the single intent given to {@link #getActivity(Context, int, Intent, int)},
+     * its content will be the subject of replacement by
+     * {@link #send(Context, int, Intent)} and {@link #FLAG_UPDATE_CURRENT}, etc.
+     * This is because it is the most specific of the supplied intents, and the
+     * UI the user actually sees when the intents are started.
+     * </p>
+     *
+     * @param context The Context in which this PendingIntent should start
+     * the activity.
+     * @param requestCode Private request code for the sender (currently
+     * not used).
+     * @param intents Array of Intents of the activities to be launched.
+     * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
+     * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
+     * or any of the flags as supported by
+     * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
+     * of the intent that can be supplied when the actual send happens.
+     *
+     * @return Returns an existing or new PendingIntent matching the given
+     * parameters.  May return null only if {@link #FLAG_NO_CREATE} has been
+     * supplied.
+     */
+    public static PendingIntent getActivities(Context context, int requestCode,
+            Intent[] intents, int flags) {
+        String packageName = context.getPackageName();
+        String[] resolvedTypes = new String[intents.length];
+        for (int i=0; i<intents.length; i++) {
+            resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
+        }
+        try {
+            IIntentSender target =
+                ActivityManagerNative.getDefault().getIntentSender(
+                    IActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                    null, null, requestCode, intents, resolvedTypes, flags);
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
         }
@@ -230,7 +290,8 @@
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     IActivityManager.INTENT_SENDER_BROADCAST, packageName,
-                    null, null, requestCode, intent, resolvedType, flags);
+                    null, null, requestCode, new Intent[] { intent },
+                    resolvedType != null ? new String[] { resolvedType } : null, flags);
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
         }
@@ -266,7 +327,8 @@
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     IActivityManager.INTENT_SENDER_SERVICE, packageName,
-                    null, null, requestCode, intent, resolvedType, flags);
+                    null, null, requestCode, new Intent[] { intent },
+                    resolvedType != null ? new String[] { resolvedType } : null, flags);
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 18602df..0c280f9 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -1109,6 +1109,9 @@
      * @return true if a successful launch, false if could not (e.g. bad position).
      */
     protected boolean launchSuggestion(int position, int actionKey, String actionMsg) {
+        if (mSuggestionsAdapter == null) {
+            return false;
+        }
         Cursor c = mSuggestionsAdapter.getCursor();
         if ((c != null) && c.moveToPosition(position)) {
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a463afc..27be51a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -725,6 +725,28 @@
     public abstract void startActivity(Intent intent);
 
     /**
+     * Launch multiple new activities.  This is generally the same as calling
+     * {@link #startActivity(Intent)} for the first Intent in the array,
+     * that activity during its creation calling {@link #startActivity(Intent)}
+     * for the second entry, etc.  Note that unlike that approach, generally
+     * none of the activities except the last in the array will be created
+     * at this point, but rather will be created when the user first visits
+     * them (due to pressing back from the activity on top).
+     *
+     * <p>This method throws {@link ActivityNotFoundException}
+     * if there was no Activity found for <em>any</em> given Intent.  In this
+     * case the state of the activity stack is undefined (some Intents in the
+     * list may be on it, some not), so you probably want to avoid such situations.
+     *
+     * @param intents An array of Intents to be started.
+     *
+     * @throws ActivityNotFoundException
+     *
+     * @see PackageManager#resolveActivity
+     */
+    public abstract void startActivities(Intent[] intents);
+
+    /**
      * Like {@link #startActivity(Intent)}, but taking a IntentSender
      * to start.  If the IntentSender is for an activity, that activity will be started
      * as if you had called the regular {@link #startActivity(Intent)}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3f5d215..f8928e4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -266,6 +266,11 @@
     }
 
     @Override
+    public void startActivities(Intent[] intents) {
+        mBase.startActivities(intents);
+    }
+
+    @Override
     public void startIntentSender(IntentSender intent,
             Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
             throws IntentSender.SendIntentException {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4b6333e..84cb2fb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2614,13 +2614,29 @@
      * animation to go to the next activity state.  This doesn't mean an
      * animation will never run -- if another activity change happens that doesn't
      * specify this flag before the activity started here is displayed, then
-     * that transition will be used.  This this flag can be put to good use
+     * that transition will be used.  This flag can be put to good use
      * when you are going to do a series of activity operations but the
      * animation seen by the user shouldn't be driven by the first activity
      * change but rather a later one.
      */
     public static final int FLAG_ACTIVITY_NO_ANIMATION = 0X00010000;
     /**
+     * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+     * this flag will cause any existing task that would be associated with the
+     * activity to be cleared before the activity is started.  That is, the activity
+     * becomes the new root of an otherwise empty task, and any old activities
+     * are finished.  This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
+     */
+    public static final int FLAG_ACTIVITY_CLEAR_TASK = 0X00008000;
+    /**
+     * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+     * this flag will cause a newly launching task to be placed on top of the current
+     * home activity task (if there is one).  That is, pressing back from the task
+     * will always return the user to home even if that was not the last activity they
+     * saw.   This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
+     */
+    public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0X00004000;
+    /**
      * If set, when sending a broadcast only registered receivers will be
      * called -- no BroadcastReceiver components will be launched.
      */
@@ -4871,18 +4887,22 @@
      * @see #FLAG_DEBUG_LOG_RESOLUTION
      * @see #FLAG_FROM_BACKGROUND
      * @see #FLAG_ACTIVITY_BROUGHT_TO_FRONT
-     * @see #FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
+     * @see #FLAG_ACTIVITY_CLEAR_TASK
      * @see #FLAG_ACTIVITY_CLEAR_TOP
+     * @see #FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
      * @see #FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
      * @see #FLAG_ACTIVITY_FORWARD_RESULT
      * @see #FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
      * @see #FLAG_ACTIVITY_MULTIPLE_TASK
      * @see #FLAG_ACTIVITY_NEW_TASK
+     * @see #FLAG_ACTIVITY_NO_ANIMATION
      * @see #FLAG_ACTIVITY_NO_HISTORY
      * @see #FLAG_ACTIVITY_NO_USER_ACTION
      * @see #FLAG_ACTIVITY_PREVIOUS_IS_TOP
      * @see #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+     * @see #FLAG_ACTIVITY_REORDER_TO_FRONT
      * @see #FLAG_ACTIVITY_SINGLE_TOP
+     * @see #FLAG_ACTIVITY_TASK_ON_HOME
      * @see #FLAG_RECEIVER_REGISTERED_ONLY
      */
     public Intent setFlags(int flags) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 7efb7fd..87f55d2 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2507,7 +2507,7 @@
                     if (pageCount > 0) {
                         dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
                                 lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
-                                db.getCachesize()));
+                                db.getCachesize(), getDataDump(db)));
                     }
                 }
                 // if there are pooled connections, return the cache stats for them also.
@@ -2518,7 +2518,7 @@
                     for (SQLiteDatabase pDb : connPool.getConnectionList()) {
                         dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
                                 + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
-                                pDb.getCacheMissNum(), pDb.getCachesize()));
+                                pDb.getCacheMissNum(), pDb.getCachesize(), null));
                     }
                 }
             } catch (SQLiteException e) {
@@ -2529,6 +2529,44 @@
         return dbStatsList;
     }
 
+    private static ArrayList<String> getDataDump(SQLiteDatabase db) {
+        // create database dump of certain data from certain databases for debugging purposes
+        if (db.getPath().equalsIgnoreCase(
+                "/data/data/com.android.providers.downloads/databases/downloads.db")) {
+            String sql =
+                    "select * from downloads " +
+                    " where notificationpackage = 'com.google.android.gsf'" + 
+                    " or status >= 400";
+            Cursor cursor = db.rawQuery(sql, null);
+            try {
+                int count = cursor.getCount();
+                if (count == 0) {
+                    return null;
+                }
+                ArrayList<String> buff = new ArrayList<String>();
+                buff.add("  Data from downloads.db");
+                int columnCount = cursor.getColumnCount();
+                for (int i =0; i < count && cursor.moveToNext(); i++) {
+                    buff.add("    Row#" + i + "");
+                    for (int j = 0; j < columnCount; j++) {
+                        String colName = cursor.getColumnName(j);
+                        String value = cursor.getString(j);
+                        buff.add("      " + colName + " = " + value);
+                    }
+                }
+                for (String s : buff)  Log.i("vnoritag", s);
+                return buff;
+            } catch (SQLiteException e) {
+                Log.w(TAG, "exception in executing the sql: " + sql, e);
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        }
+        return null;
+    }
+
     /**
      * Returns list of full pathnames of all attached databases including the main database
      * by executing 'pragma database_list' on the database.
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 94960791..72377f0 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -121,27 +121,31 @@
      */
     public static class DbStats {
         /** name of the database */
-        public String dbName;
+        public final String dbName;
 
         /** the page size for the database */
-        public long pageSize;
+        public final long pageSize;
 
         /** the database size */
-        public long dbSize;
+        public final long dbSize;
 
         /** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */
-        public int lookaside;
+        public final int lookaside;
 
         /** statement cache stats: hits/misses/cachesize */
-        public String cache;
+        public final String cache;
+
+        /** database dump of 'useful info for debugging only */
+        public final ArrayList<String> dataDump;
 
         public DbStats(String dbName, long pageCount, long pageSize, int lookaside,
-            int hits, int misses, int cachesize) {
+            int hits, int misses, int cachesize, ArrayList<String> data) {
             this.dbName = dbName;
             this.pageSize = pageSize / 1024;
             dbSize = (pageCount * pageSize) / 1024;
             this.lookaside = lookaside;
             this.cache = hits + "/" + misses + "/" + cachesize;
+            this.dataDump = data;
         }
     }
 
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 378189e..f3b2c81 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -906,6 +906,7 @@
      * Changes the settings for this Camera service.
      *
      * @param params the Parameters to use for this Camera service
+     * @throws RuntimeException if any parameter is invalid or not supported.
      * @see #getParameters()
      */
     public void setParameters(Parameters params) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 2b4f39a..1c295a7 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -513,21 +513,27 @@
     }
 
     /**
-     * Count the number and aggregate size of memory allocations between
-     * two points.
+     * Start counting the number and aggregate size of memory allocations.
      *
-     * The "start" function resets the counts and enables counting.  The
-     * "stop" function disables the counting so that the analysis code
-     * doesn't cause additional allocations.  The "get" function returns
-     * the specified value.
+     * <p>The {@link #startAllocCounting() start} function resets the counts and enables counting.
+     * The {@link #stopAllocCounting() stop} function disables the counting so that the analysis
+     * code doesn't cause additional allocations.  The various <code>get</code> functions return
+     * the specified value. And the various <code>reset</code> functions reset the specified
+     * count.</p>
      *
-     * Counts are kept for the system as a whole and for each thread.
+     * <p>Counts are kept for the system as a whole and for each thread.
      * The per-thread counts for threads other than the current thread
-     * are not cleared by the "reset" or "start" calls.
+     * are not cleared by the "reset" or "start" calls.</p>
      */
     public static void startAllocCounting() {
         VMDebug.startAllocCounting();
     }
+
+    /**
+     * Stop counting the number and aggregate size of memory allocations.
+     *
+     * @see #startAllocCounting()
+     */
     public static void stopAllocCounting() {
         VMDebug.stopAllocCounting();
     }
@@ -671,11 +677,11 @@
      * for catching regressions in code that is expected to operate
      * without causing any allocations.
      *
-     * Pass in the maximum number of allowed allocations.  Use -1 to disable
-     * the limit.  Returns the previous limit.
+     * <p>Pass in the maximum number of allowed allocations.  Use -1 to disable
+     * the limit.  Returns the previous limit.</p>
      *
-     * The preferred way to use this is:
-     *
+     * <p>The preferred way to use this is:
+     * <pre>
      *  int prevLimit = -1;
      *  try {
      *      prevLimit = Debug.setAllocationLimit(0);
@@ -683,16 +689,16 @@
      *  } finally {
      *      Debug.setAllocationLimit(prevLimit);
      *  }
-     *
+     * </pre>
      * This allows limits to be nested.  The try/finally ensures that the
-     * limit is reset if something fails.
+     * limit is reset if something fails.</p>
      *
-     * Exceeding the limit causes a dalvik.system.AllocationLimitError to
+     * <p>Exceeding the limit causes a dalvik.system.AllocationLimitError to
      * be thrown from a memory allocation call.  The limit is reset to -1
-     * when this happens.
+     * when this happens.</p>
      *
-     * The feature may be disabled in the VM configuration.  If so, this
-     * call has no effect, and always returns -1.
+     * <p>The feature may be disabled in the VM configuration.  If so, this
+     * call has no effect, and always returns -1.</p>
      */
     public static int setAllocationLimit(int limit) {
         return VMDebug.setAllocationLimit(limit);
@@ -846,6 +852,7 @@
      * API for gathering and querying instruction counts.
      *
      * Example usage:
+     * <pre>
      *   Debug.InstructionCount icount = new Debug.InstructionCount();
      *   icount.resetAndStart();
      *    [... do lots of stuff ...]
@@ -855,6 +862,7 @@
      *       System.out.println("Method invocations: "
      *           + icount.globalMethodInvocations());
      *   }
+     * </pre>
      */
     public static class InstructionCount {
         private static final int NUM_INSTR = 256;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index c836e56..b69e0b1 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -22,6 +22,8 @@
 import android.content.Intent;
 import android.util.Log;
 import android.util.Printer;
+import android.util.Singleton;
+import android.view.IWindowManager;
 
 import com.android.internal.os.RuntimeInit;
 
@@ -32,6 +34,7 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * <p>StrictMode is a developer tool which detects things you might be
@@ -107,6 +110,7 @@
     private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE);
 
     private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
+    private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
 
     // Only log a duplicate stack trace to the logs every second.
     private static final long MIN_LOG_INTERVAL_MS = 1000;
@@ -179,6 +183,13 @@
     public static final int PENALTY_DEATH_ON_NETWORK = 0x200;
 
     /**
+     * Flash the screen during violations.
+     *
+     * @hide
+     */
+    public static final int PENALTY_FLASH = 0x800;
+
+    /**
      * @hide
      */
     public static final int PENALTY_DROPBOX = 0x80;
@@ -201,13 +212,19 @@
      */
     private static final int PENALTY_MASK =
             PENALTY_LOG | PENALTY_DIALOG | PENALTY_DEATH | PENALTY_DROPBOX | PENALTY_GATHER |
-            PENALTY_DEATH_ON_NETWORK;
+            PENALTY_DEATH_ON_NETWORK | PENALTY_FLASH;
 
     /**
      * The current VmPolicy in effect.
      */
     private static volatile int sVmPolicyMask = 0;
 
+    /**
+     * The number of threads trying to do an async dropbox write.
+     * Just to limit ourselves out of paranoia.
+     */
+    private static final AtomicInteger sDropboxCallsInFlight = new AtomicInteger(0);
+
     private StrictMode() {}
 
     /**
@@ -370,6 +387,13 @@
             }
 
             /**
+             * Flash the screen during a violation.
+             */
+            public Builder penaltyFlashScreen() {
+                return enable(PENALTY_FLASH);
+            }
+
+            /**
              * Log detected violations to the system log.
              */
             public Builder penaltyLog() {
@@ -703,7 +727,9 @@
             StrictMode.DETECT_DISK_WRITE |
             StrictMode.DETECT_DISK_READ |
             StrictMode.DETECT_NETWORK |
-            StrictMode.PENALTY_DROPBOX);
+            StrictMode.PENALTY_DROPBOX |
+            (IS_ENG_BUILD ? StrictMode.PENALTY_FLASH : 0)
+        );
         sVmPolicyMask = StrictMode.DETECT_VM_CURSOR_LEAKS |
                 StrictMode.DETECT_VM_CLOSABLE_LEAKS |
                 StrictMode.PENALTY_DROPBOX;
@@ -897,6 +923,15 @@
                 return;
             }
 
+            final IWindowManager windowManager = (info.policy & PENALTY_FLASH) != 0 ?
+                    sWindowManager.get() : null;
+            if (windowManager != null) {
+                try {
+                    windowManager.showStrictModeViolation(true);
+                } catch (RemoteException unused) {
+                }
+            }
+
             queue.addIdleHandler(new MessageQueue.IdleHandler() {
                     public boolean queueIdle() {
                         long loopFinishTime = SystemClock.uptimeMillis();
@@ -908,6 +943,12 @@
                             handleViolation(v);
                         }
                         records.clear();
+                        if (windowManager != null) {
+                            try {
+                                windowManager.showStrictModeViolation(false);
+                            } catch (RemoteException unused) {
+                            }
+                        }
                         return false;  // remove this idle handler from the array
                     }
                 });
@@ -946,7 +987,7 @@
             }
 
             // Not perfect, but fast and good enough for dup suppression.
-            Integer crashFingerprint = info.crashInfo.stackTrace.hashCode();
+            Integer crashFingerprint = info.hashCode();
             long lastViolationTime = 0;
             if (mLastViolationTime.containsKey(crashFingerprint)) {
                 lastViolationTime = mLastViolationTime.get(crashFingerprint);
@@ -984,7 +1025,6 @@
             if (violationMaskSubset != 0) {
                 int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage);
                 violationMaskSubset |= violationBit;
-                final int violationMaskSubsetFinal = violationMaskSubset;
                 final int savedPolicyMask = getThreadPolicyMask();
 
                 final boolean justDropBox = (info.policy & PENALTY_MASK) == PENALTY_DROPBOX;
@@ -995,20 +1035,7 @@
                     // call synchronously which Binder data suggests
                     // isn't always super fast, despite the implementation
                     // in the ActivityManager trying to be mostly async.
-                    new Thread("callActivityManagerForStrictModeDropbox") {
-                        public void run() {
-                            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                            try {
-                                ActivityManagerNative.getDefault().
-                                        handleApplicationStrictModeViolation(
-                                            RuntimeInit.getApplicationObject(),
-                                            violationMaskSubsetFinal,
-                                            info);
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "RemoteException handling StrictMode violation", e);
-                            }
-                        }
-                    }.start();
+                    dropboxViolationAsync(violationMaskSubset, info);
                     return;
                 }
 
@@ -1040,6 +1067,44 @@
         }
     }
 
+    /**
+     * In the common case, as set by conditionallyEnableDebugLogging,
+     * we're just dropboxing any violations but not showing a dialog,
+     * not loggging, and not killing the process.  In these cases we
+     * don't need to do a synchronous call to the ActivityManager.
+     * This is used by both per-thread and vm-wide violations when
+     * applicable.
+     */
+    private static void dropboxViolationAsync(
+            final int violationMaskSubset, final ViolationInfo info) {
+        int outstanding = sDropboxCallsInFlight.incrementAndGet();
+        if (outstanding > 20) {
+            // What's going on?  Let's not make make the situation
+            // worse and just not log.
+            sDropboxCallsInFlight.decrementAndGet();
+            return;
+        }
+
+        if (LOG_V) Log.d(TAG, "Dropboxing async; in-flight=" + outstanding);
+
+        new Thread("callActivityManagerForStrictModeDropbox") {
+            public void run() {
+                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                try {
+                    ActivityManagerNative.getDefault().
+                            handleApplicationStrictModeViolation(
+                                RuntimeInit.getApplicationObject(),
+                                violationMaskSubset,
+                                info);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException handling StrictMode violation", e);
+                }
+                int outstanding = sDropboxCallsInFlight.decrementAndGet();
+                if (LOG_V) Log.d(TAG, "Dropbox complete; in-flight=" + outstanding);
+            }
+        }.start();
+    }
+
     private static class AndroidCloseGuardReporter implements CloseGuard.Reporter {
         public void report (String message, Throwable allocationSite) {
             onVmPolicyViolation(message, allocationSite);
@@ -1130,14 +1195,25 @@
             Log.e(TAG, message, originStack);
         }
 
-        if ((sVmPolicyMask & PENALTY_DROPBOX) != 0) {
-            final ViolationInfo info = new ViolationInfo(originStack, sVmPolicyMask);
+        boolean penaltyDropbox = (sVmPolicyMask & PENALTY_DROPBOX) != 0;
+        boolean penaltyDeath = (sVmPolicyMask & PENALTY_DEATH) != 0;
 
+        int violationMaskSubset = PENALTY_DROPBOX | DETECT_VM_CURSOR_LEAKS;
+        ViolationInfo info = new ViolationInfo(originStack, sVmPolicyMask);
+
+        if (penaltyDropbox && !penaltyDeath) {
+            // Common case for userdebug/eng builds.  If no death and
+            // just dropboxing, we can do the ActivityManager call
+            // asynchronously.
+            dropboxViolationAsync(violationMaskSubset, info);
+            return;
+        }
+
+        if (penaltyDropbox) {
             // The violationMask, passed to ActivityManager, is a
             // subset of the original StrictMode policy bitmask, with
             // only the bit violated and penalty bits to be executed
             // by the ActivityManagerService remaining set.
-            int violationMaskSubset = PENALTY_DROPBOX | DETECT_VM_CURSOR_LEAKS;
             final int savedPolicyMask = getThreadPolicyMask();
             try {
                 // First, remove any policy before we call into the Activity Manager,
@@ -1158,7 +1234,7 @@
             }
         }
 
-        if ((sVmPolicyMask & PENALTY_DEATH) != 0) {
+        if (penaltyDeath) {
             System.err.println("StrictMode VmPolicy violation with POLICY_DEATH; shutting down.");
             Process.killProcess(Process.myPid());
             System.exit(10);
@@ -1327,6 +1403,12 @@
         }
     };
 
+    private static Singleton<IWindowManager> sWindowManager = new Singleton<IWindowManager>() {
+        protected IWindowManager create() {
+            return IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+        }
+    };
+
     /**
      * Enter a named critical span (e.g. an animation)
      *
@@ -1468,6 +1550,24 @@
             }
         }
 
+        @Override
+        public int hashCode() {
+            int result = 17;
+            result = 37 * result + crashInfo.stackTrace.hashCode();
+            if (numAnimationsRunning != 0) {
+                result *= 37;
+            }
+            if (broadcastIntentAction != null) {
+                result = 37 * result + broadcastIntentAction.hashCode();
+            }
+            if (tags != null) {
+                for (String tag : tags) {
+                    result = 37 * result + tag.hashCode();
+                }
+            }
+            return result;
+        }
+
         /**
          * Create an instance of ViolationInfo initialized from a Parcel.
          */
diff --git a/core/java/android/preference/PreferenceFrameLayout.java b/core/java/android/preference/PreferenceFrameLayout.java
new file mode 100644
index 0000000..426abf0
--- /dev/null
+++ b/core/java/android/preference/PreferenceFrameLayout.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.preference;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * @hide
+ */
+public class PreferenceFrameLayout extends FrameLayout {
+    private static final int DEFAULT_TOP_PADDING = 0;
+    private static final int DEFAULT_BOTTOM_PADDING = 0;
+    private final int mTopPadding;
+    private final int mBottomPadding;
+    private boolean mPaddingApplied = false;
+
+    public PreferenceFrameLayout(Context context) {
+        this(context, null);
+    }
+
+    public PreferenceFrameLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.preferenceFrameLayoutStyle);
+    }
+
+    public PreferenceFrameLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.PreferenceFrameLayout, defStyle, 0);
+
+        mTopPadding = (int) a.getDimension(
+                com.android.internal.R.styleable.PreferenceFrameLayout_topPadding,
+                DEFAULT_TOP_PADDING);
+        mBottomPadding = (int) a.getDimension(
+                com.android.internal.R.styleable.PreferenceFrameLayout_bottomPadding,
+                DEFAULT_BOTTOM_PADDING);
+
+        a.recycle();
+    }
+
+    @Override
+    public void addView(View child) {
+        int topPadding = getPaddingTop();
+        int bottomPadding = getPaddingBottom();
+        // Check on the id of the child before adding it.
+        if (child != null && child.getId() != com.android.internal.R.id.default_preference_layout) {
+            // Add the padding to the view group after determining if the padding already exists.
+            if (!mPaddingApplied) {
+                topPadding += mTopPadding;
+                bottomPadding += mBottomPadding;
+                mPaddingApplied = true;
+            }
+        } else {
+            if (mPaddingApplied) {
+                topPadding -= mTopPadding;
+                bottomPadding -= mBottomPadding;
+                mPaddingApplied = false;
+            }
+        }
+        int previousTop = getPaddingTop();
+        int previousBottom = getPaddingBottom();
+        if (previousTop != topPadding || previousBottom != bottomPadding) {
+            setPadding(getPaddingLeft(), topPadding, getPaddingRight(), bottomPadding);
+        }
+        super.addView(child);
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 2d2f205..d1ca0c9 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -5669,6 +5669,13 @@
          * Type: INTEGER (boolean)
          */
         public static final String FAVORITES = "favorites";
+
+        /**
+         * The "read-only" flag: "0" by default, "1" if the row cannot be modified or
+         * deleted except by a sync adapter.  See {@link ContactsContract#CALLER_IS_SYNCADAPTER}.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String GROUP_IS_READ_ONLY = "group_is_read_only";
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index f111ef2..fb4bed7 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -53,6 +53,13 @@
 
     private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
 
+   /**
+     * Broadcast Action:  A broadcast to indicate the end of an MTP session with the host.
+     * This broadcast is only sent if MTP activity has modified the media database during the
+     * most recent MTP session.
+     */
+    public static final String ACTION_MTP_SESSION_END = "android.provider.action.MTP_SESSION_END";
+
     /**
      * Activity Action: Launch a music player.
      * The activity should be able to play, browse, or manipulate music files stored on the device.
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Ptp.java
similarity index 89%
rename from core/java/android/provider/Mtp.java
rename to core/java/android/provider/Ptp.java
index 78110ef..2c54370 100644
--- a/core/java/android/provider/Mtp.java
+++ b/core/java/android/provider/Ptp.java
@@ -22,28 +22,20 @@
 
 
 /**
- * The MTP provider supports accessing content on MTP and PTP devices.
+ * The PTP provider supports accessing content on PTP devices.
  * @hide
  */
-public final class Mtp
+public final class Ptp
 {
-    private final static String TAG = "Mtp";
+    private final static String TAG = "Ptp";
 
-    public static final String AUTHORITY = "mtp";
+    public static final String AUTHORITY = "ptp";
 
     private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
     private static final String CONTENT_AUTHORITY_DEVICE_SLASH = "content://" + AUTHORITY + "/device/";
 
-
-   /**
-     * Broadcast Action:  A broadcast to indicate the end of an MTP session with the host.
-     * This broadcast is only sent if MTP activity has modified the media database during the
-     * most recent MTP session
-     */
-    public static final String ACTION_MTP_SESSION_END = "android.provider.action.MTP_SESSION_END";
-
     /**
-     * Contains list of all MTP/PTP devices
+     * Contains list of all PTP devices
      */
     public static final class Device implements BaseColumns {
 
@@ -67,7 +59,7 @@
     }
 
     /**
-     * Contains list of storage units for an MTP/PTP device
+     * Contains list of storage units for an PTP device
      */
     public static final class Storage implements BaseColumns {
 
@@ -93,7 +85,7 @@
     }
 
     /**
-     * Contains list of objects on an MTP/PTP device
+     * Contains list of objects on an PTP device
      */
     public static final class Object implements BaseColumns {
 
@@ -133,7 +125,7 @@
 
         /**
          * The following columns correspond to the fields in the ObjectInfo dataset
-         * as described in the MTP specification.
+         * as described in the PTP specification.
          */
 
         /**
@@ -144,7 +136,7 @@
 
         /**
          * The object's format.  Can be one of the FORMAT_* symbols below,
-         * or any of the valid MTP object formats as defined in the MTP specification.
+         * or any of the valid PTP object formats as defined in the PTP specification.
          * <P>Type: INTEGER</P>
          */
         public static final String FORMAT = "format";
@@ -163,7 +155,7 @@
 
         /**
          * The object's thumbnail format.  Can be one of the FORMAT_* symbols below,
-         * or any of the valid MTP object formats as defined in the MTP specification.
+         * or any of the valid PTP object formats as defined in the PTP specification.
          * <P>Type: INTEGER</P>
          */
         public static final String THUMB_FORMAT = "thumb_format";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7075774..4ea4a16 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3318,20 +3318,6 @@
                 "pdp_watchdog_max_pdp_reset_fail_count";
 
         /**
-         * Address to ping as a last sanity check before attempting any recovery.
-         * Unset or set to "0.0.0.0" to skip this check.
-         * @hide
-         */
-        public static final String PDP_WATCHDOG_PING_ADDRESS = "pdp_watchdog_ping_address";
-
-        /**
-         * The "-w deadline" parameter for the ping, ie, the max time in
-         * seconds to spend pinging.
-         * @hide
-         */
-        public static final String PDP_WATCHDOG_PING_DEADLINE = "pdp_watchdog_ping_deadline";
-
-        /**
          * The interval in milliseconds at which to check gprs registration
          * after the first registration mismatch of gprs and voice service,
          * to detect possible data network registration problems.
diff --git a/core/java/android/util/Singleton.java b/core/java/android/util/Singleton.java
new file mode 100644
index 0000000..8a38bdb
--- /dev/null
+++ b/core/java/android/util/Singleton.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Singleton helper class for lazily initialization.
+ *
+ * Modeled after frameworks/base/include/utils/Singleton.h
+ *
+ * @hide
+ */
+public abstract class Singleton<T> {
+    private T mInstance;
+
+    protected abstract T create();
+
+    public final T get() {
+        synchronized (this) {
+            if (mInstance == null) {
+                mInstance = create();
+            }
+            return mInstance;
+        }
+    }
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index dd0f477..d964e2f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -122,7 +122,7 @@
     int getTrackballKeycodeState(int sw);
     int getDPadKeycodeState(int sw);
     InputChannel monitorInput(String inputChannelName);
-    
+
     // Report whether the hardware supports the given keys; returns true if successful
     boolean hasKeys(in int[] keycodes, inout boolean[] keyExists);
     
@@ -132,7 +132,14 @@
     
     // For testing
     void setInTouchMode(boolean showFocus);
-    
+
+    // For StrictMode flashing a red border on violations from the UI
+    // thread.  The uid/pid is implicit from the Binder call, and the Window
+    // Manager uses that to determine whether or not the red border should
+    // actually be shown.  (it will be ignored that pid doesn't have windows
+    // on screen)
+    void showStrictModeViolation(boolean on);
+
     // These can only be called with the SET_ORIENTATION permission.
     /**
      * Change the current screen rotation, constants as per
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e6eb46e..011ad77 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1627,6 +1627,40 @@
     static final int ACTIVATED                    = 0x40000000;
 
     /**
+     * Always allow a user to over-scroll this view, provided it is a
+     * view that can scroll.
+     *
+     * @see #getOverScrollMode()
+     * @see #setOverScrollMode(int)
+     */
+    public static final int OVER_SCROLL_ALWAYS = 0;
+
+    /**
+     * Allow a user to over-scroll this view only if the content is large
+     * enough to meaningfully scroll, provided it is a view that can scroll.
+     *
+     * @see #getOverScrollMode()
+     * @see #setOverScrollMode(int)
+     */
+    public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;
+
+    /**
+     * Never allow a user to over-scroll this view.
+     *
+     * @see #getOverScrollMode()
+     * @see #setOverScrollMode(int)
+     */
+    public static final int OVER_SCROLL_NEVER = 2;
+
+    /**
+     * Controls the over-scroll mode for this view.
+     * See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
+     * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
+     * and {@link #OVER_SCROLL_NEVER}.
+     */
+    private int mOverScrollMode;
+
+    /**
      * The parent this view is attached to.
      * {@hide}
      *
@@ -2057,6 +2091,7 @@
         mResources = context != null ? context.getResources() : null;
         mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
     }
 
     /**
@@ -2122,6 +2157,7 @@
 
         int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
 
+        int overScrollMode = mOverScrollMode;
         final int N = a.getIndexCount();
         for (int i = 0; i < N; i++) {
             int attr = a.getIndex(i);
@@ -2327,9 +2363,14 @@
                         });
                     }
                     break;
+                case R.styleable.View_overScrollMode:
+                    overScrollMode = a.getInt(attr, OVER_SCROLL_IF_CONTENT_SCROLLS);
+                    break;
             }
         }
 
+        setOverScrollMode(overScrollMode);
+
         if (background != null) {
             setBackgroundDrawable(background);
         }
@@ -10131,6 +10172,128 @@
     }
 
     /**
+     * Scroll the view with standard behavior for scrolling beyond the normal
+     * content boundaries. Views that call this method should override
+     * {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the
+     * results of an over-scroll operation.
+     *
+     * Views can use this method to handle any touch or fling-based scrolling.
+     *
+     * @param deltaX Change in X in pixels
+     * @param deltaY Change in Y in pixels
+     * @param scrollX Current X scroll value in pixels before applying deltaX
+     * @param scrollY Current Y scroll value in pixels before applying deltaY
+     * @param scrollRangeX Maximum content scroll range along the X axis
+     * @param scrollRangeY Maximum content scroll range along the Y axis
+     * @param maxOverScrollX Number of pixels to overscroll by in either direction
+     *          along the X axis.
+     * @param maxOverScrollY Number of pixels to overscroll by in either direction
+     *          along the Y axis.
+     * @param isTouchEvent true if this scroll operation is the result of a touch event.
+     * @return true if scrolling was clamped to an over-scroll boundary along either
+     *          axis, false otherwise.
+     */
+    protected boolean overScrollBy(int deltaX, int deltaY,
+            int scrollX, int scrollY,
+            int scrollRangeX, int scrollRangeY,
+            int maxOverScrollX, int maxOverScrollY,
+            boolean isTouchEvent) {
+        final int overScrollMode = mOverScrollMode;
+        final boolean canScrollHorizontal =
+                computeHorizontalScrollRange() > computeHorizontalScrollExtent();
+        final boolean canScrollVertical =
+                computeVerticalScrollRange() > computeVerticalScrollExtent();
+        final boolean overScrollHorizontal = overScrollMode == OVER_SCROLL_ALWAYS ||
+                (overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
+        final boolean overScrollVertical = overScrollMode == OVER_SCROLL_ALWAYS ||
+                (overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
+
+        int newScrollX = scrollX + deltaX;
+        if (!overScrollHorizontal) {
+            maxOverScrollX = 0;
+        }
+
+        int newScrollY = scrollY + deltaY;
+        if (!overScrollVertical) {
+            maxOverScrollY = 0;
+        }
+
+        // Clamp values if at the limits and record
+        final int left = -maxOverScrollX;
+        final int right = maxOverScrollX + scrollRangeX;
+        final int top = -maxOverScrollY;
+        final int bottom = maxOverScrollY + scrollRangeY;
+
+        boolean clampedX = false;
+        if (newScrollX > right) {
+            newScrollX = right;
+            clampedX = true;
+        } else if (newScrollX < left) {
+            newScrollX = left;
+            clampedX = true;
+        }
+
+        boolean clampedY = false;
+        if (newScrollY > bottom) {
+            newScrollY = bottom;
+            clampedY = true;
+        } else if (newScrollY < top) {
+            newScrollY = top;
+            clampedY = true;
+        }
+
+        onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
+
+        return clampedX || clampedY;
+    }
+
+    /**
+     * Called by {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)} to
+     * respond to the results of an over-scroll operation.
+     *
+     * @param scrollX New X scroll value in pixels
+     * @param scrollY New Y scroll value in pixels
+     * @param clampedX True if scrollX was clamped to an over-scroll boundary
+     * @param clampedY True if scrollY was clamped to an over-scroll boundary
+     */
+    protected void onOverScrolled(int scrollX, int scrollY,
+            boolean clampedX, boolean clampedY) {
+        // Intentionally empty.
+    }
+
+    /**
+     * Returns the over-scroll mode for this view. The result will be
+     * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
+     * (allow over-scrolling only if the view content is larger than the container),
+     * or {@link #OVER_SCROLL_NEVER}.
+     *
+     * @return This view's over-scroll mode.
+     */
+    public int getOverScrollMode() {
+        return mOverScrollMode;
+    }
+
+    /**
+     * Set the over-scroll mode for this view. Valid over-scroll modes are
+     * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
+     * (allow over-scrolling only if the view content is larger than the container),
+     * or {@link #OVER_SCROLL_NEVER}.
+     *
+     * Setting the over-scroll mode of a view will have an effect only if the
+     * view is capable of scrolling.
+     *
+     * @param overScrollMode The new over-scroll mode for this view.
+     */
+    public void setOverScrollMode(int overScrollMode) {
+        if (overScrollMode != OVER_SCROLL_ALWAYS &&
+                overScrollMode != OVER_SCROLL_IF_CONTENT_SCROLLS &&
+                overScrollMode != OVER_SCROLL_NEVER) {
+            throw new IllegalArgumentException("Invalid overscroll mode " + overScrollMode);
+        }
+        mOverScrollMode = overScrollMode;
+    }
+
+    /**
      * A MeasureSpec encapsulates the layout requirements passed from parent to child.
      * Each MeasureSpec represents a requirement for either the width or the height.
      * A MeasureSpec is comprised of a size and a mode. There are three possible
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 85981d2..bb85894 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -144,7 +144,7 @@
     /**
      * Maximum velocity to initiate a fling, as measured in pixels per second
      */
-    private static final int MAXIMUM_FLING_VELOCITY = 4000;
+    private static final int MAXIMUM_FLING_VELOCITY = 8000;
 
     /**
      * The maximum size of View's drawing cache, expressed in bytes. This size
@@ -158,6 +158,16 @@
      */
     private static float SCROLL_FRICTION = 0.015f;
 
+    /**
+     * Max distance to overscroll for edge effects
+     */
+    private static final int OVERSCROLL_DISTANCE = 0;
+
+    /**
+     * Max distance to overfling for edge effects
+     */
+    private static final int OVERFLING_DISTANCE = 4;
+
     private final int mEdgeSlop;
     private final int mFadingEdgeLength;
     private final int mMinimumFlingVelocity;
@@ -168,6 +178,8 @@
     private final int mDoubleTapSlop;
     private final int mWindowTouchSlop;
     private final int mMaximumDrawingCacheSize;
+    private final int mOverscrollDistance;
+    private final int mOverflingDistance;
 
     private static final SparseArray<ViewConfiguration> sConfigurations =
             new SparseArray<ViewConfiguration>(2);
@@ -188,6 +200,8 @@
         mWindowTouchSlop = WINDOW_TOUCH_SLOP;
         //noinspection deprecation
         mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
+        mOverscrollDistance = OVERSCROLL_DISTANCE;
+        mOverflingDistance = OVERFLING_DISTANCE;
     }
 
     /**
@@ -216,6 +230,9 @@
 
         // Size of the screen in bytes, in ARGB_8888 format
         mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels;
+
+        mOverscrollDistance = (int) (density * OVERSCROLL_DISTANCE + 0.5f);
+        mOverflingDistance = (int) (density * OVERFLING_DISTANCE + 0.5f);
     }
 
     /**
@@ -473,6 +490,20 @@
     }
 
     /**
+     * @return The maximum distance a View should overscroll by when showing edge effects.
+     */
+    public int getScaledOverscrollDistance() {
+        return mOverscrollDistance;
+    }
+
+    /**
+     * @return The maximum distance a View should overfling by when showing edge effects.
+     */
+    public int getScaledOverflingDistance() {
+        return mOverflingDistance;
+    }
+
+    /**
      * The amount of time that the zoom controls should be
      * displayed on the screen expressed in milliseconds.
      * 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index fe55f34..621a908 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -47,7 +47,10 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -1452,6 +1455,38 @@
         }
     }
 
+    public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
+        synchronized (mH) {
+            HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
+                    new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
+            try {
+                // TODO: We should change the return type from List<Object> to List<Parcelable>
+                List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
+                // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
+                ArrayList<InputMethodSubtype> subtypes = null;
+                final int N = info.size();
+                if (info != null && N > 0) {
+                    for (int i = 0; i < N; ++i) {
+                        Object o = info.get(i);
+                        if (o instanceof InputMethodInfo) {
+                            if (ret.containsKey(o)) {
+                                Log.e(TAG, "IMI list already contains the same InputMethod.");
+                                break;
+                            }
+                            subtypes = new ArrayList<InputMethodSubtype>();
+                            ret.put((InputMethodInfo)o, subtypes);
+                        } else if (subtypes != null && o instanceof InputMethodSubtype) {
+                            subtypes.add((InputMethodSubtype)o);
+                        }
+                    }
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+            }
+            return ret;
+        }
+    }
+
     public boolean switchToLastInputMethod(IBinder imeToken) {
         synchronized (mH) {
             try {
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 61a30ab..9568e4f 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -780,50 +780,11 @@
             if (cacheMode == WebSettings.LOAD_NORMAL) {
                 cacheMode = WebSettings.LOAD_NO_CACHE;
             }
-            if (mSettings.getSavePassword() && hasPasswordField()) {
-                try {
-                    if (DebugFlags.BROWSER_FRAME) {
-                        Assert.assertNotNull(mCallbackProxy.getBackForwardList()
-                                .getCurrentItem());
-                    }
-                    WebAddress uri = new WebAddress(mCallbackProxy
-                            .getBackForwardList().getCurrentItem().getUrl());
-                    String schemePlusHost = uri.getScheme() + uri.getHost();
-                    String[] ret = getUsernamePassword();
-                    // Has the user entered a username/password pair and is
-                    // there some POST data
-                    if (ret != null && postData != null && 
-                            ret[0].length() > 0 && ret[1].length() > 0) {
-                        // Check to see if the username & password appear in
-                        // the post data (there could be another form on the
-                        // page and that was posted instead.
-                        String postString = new String(postData);
-                        if (postString.contains(URLEncoder.encode(ret[0])) &&
-                                postString.contains(URLEncoder.encode(ret[1]))) {
-                            String[] saved = mDatabase.getUsernamePassword(
-                                    schemePlusHost);
-                            if (saved != null) {
-                                // null username implies that user has chosen not to
-                                // save password
-                                if (saved[0] != null) {
-                                    // non-null username implies that user has
-                                    // chosen to save password, so update the 
-                                    // recorded password
-                                    mDatabase.setUsernamePassword(
-                                            schemePlusHost, ret[0], ret[1]);
-                                }
-                            } else {
-                                // CallbackProxy will handle creating the resume
-                                // message
-                                mCallbackProxy.onSavePassword(schemePlusHost, ret[0], 
-                                        ret[1], null);
-                            }
-                        }
-                    }
-                } catch (ParseException ex) {
-                    // if it is bad uri, don't save its password
-                }
-                
+            String[] ret = getUsernamePassword();
+            if (ret != null) {
+                String domUsername = ret[0];
+                String domPassword = ret[1];
+                maybeSavePassword(postData, domUsername, domPassword);
             }
         }
 
@@ -874,6 +835,68 @@
         return !synchronous ? loadListener : null;
     }
 
+    /**
+     * If this looks like a POST request (form submission) containing a username
+     * and password, give the user the option of saving them. Will either do
+     * nothing, or block until the UI interaction is complete.
+     *
+     * Called by startLoadingResource when using the Apache HTTP stack.
+     * Called directly by WebKit when using the Chrome HTTP stack.
+     *
+     * @param postData The data about to be sent as the body of a POST request.
+     * @param username The username entered by the user (sniffed from the DOM).
+     * @param password The password entered by the user (sniffed from the DOM).
+     */
+    private void maybeSavePassword(
+            byte[] postData, String username, String password) {
+        if (postData == null
+                || username == null || username.isEmpty()
+                || password == null || password.isEmpty()) {
+            return; // No password to save.
+        }
+
+        if (!mSettings.getSavePassword()) {
+            return; // User doesn't want to save passwords.
+        }
+
+        try {
+            if (DebugFlags.BROWSER_FRAME) {
+                Assert.assertNotNull(mCallbackProxy.getBackForwardList()
+                        .getCurrentItem());
+            }
+            WebAddress uri = new WebAddress(mCallbackProxy
+                    .getBackForwardList().getCurrentItem().getUrl());
+            String schemePlusHost = uri.getScheme() + uri.getHost();
+            // Check to see if the username & password appear in
+            // the post data (there could be another form on the
+            // page and that was posted instead.
+            String postString = new String(postData);
+            if (postString.contains(URLEncoder.encode(username)) &&
+                    postString.contains(URLEncoder.encode(password))) {
+                String[] saved = mDatabase.getUsernamePassword(
+                        schemePlusHost);
+                if (saved != null) {
+                    // null username implies that user has chosen not to
+                    // save password
+                    if (saved[0] != null) {
+                        // non-null username implies that user has
+                        // chosen to save password, so update the
+                        // recorded password
+                        mDatabase.setUsernamePassword(
+                                schemePlusHost, username, password);
+                    }
+                } else {
+                    // CallbackProxy will handle creating the resume
+                    // message
+                    mCallbackProxy.onSavePassword(schemePlusHost, username,
+                            password, null);
+                }
+            }
+        } catch (ParseException ex) {
+            // if it is bad uri, don't save its password
+        }
+    }
+
     // Called by jni from the chrome network stack.
     private WebResourceResponse shouldInterceptRequest(String url) {
         InputStream androidResource = inputStreamForAndroidResource(url);
diff --git a/core/java/android/webkit/OverScrollGlow.java b/core/java/android/webkit/OverScrollGlow.java
new file mode 100644
index 0000000..53600f6
--- /dev/null
+++ b/core/java/android/webkit/OverScrollGlow.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+import com.android.internal.R;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.EdgeGlow;
+
+/**
+ * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges.
+ * @hide
+ */
+public class OverScrollGlow {
+    private WebView mHostView;
+
+    private EdgeGlow mEdgeGlowTop;
+    private EdgeGlow mEdgeGlowBottom;
+    private EdgeGlow mEdgeGlowLeft;
+    private EdgeGlow mEdgeGlowRight;
+
+    private int mOverScrollDeltaX;
+    private int mOverScrollDeltaY;
+
+    public OverScrollGlow(WebView host) {
+        mHostView = host;
+        final Resources res = host.getContext().getResources();
+        final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
+        final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
+        mEdgeGlowTop = new EdgeGlow(edge, glow);
+        mEdgeGlowBottom = new EdgeGlow(edge, glow);
+        mEdgeGlowLeft = new EdgeGlow(edge, glow);
+        mEdgeGlowRight = new EdgeGlow(edge, glow);
+    }
+
+    /**
+     * Pull leftover touch scroll distance into one of the edge glows as appropriate.
+     *
+     * @param x Current X scroll offset
+     * @param y Current Y scroll offset
+     * @param oldX Old X scroll offset
+     * @param oldY Old Y scroll offset
+     * @param maxX Maximum range for horizontal scrolling
+     * @param maxY Maximum range for vertical scrolling
+     */
+    public void pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY) {
+        // Only show overscroll bars if there was no movement in any direction
+        // as a result of scrolling.
+        if (oldX == mHostView.getScrollX() && oldY == mHostView.getScrollY()) {
+            // Don't show left/right glows if we fit the whole content.
+            // Also don't show if there was vertical movement.
+            if (maxX > 0) {
+                final int pulledToX = oldX + mOverScrollDeltaX;
+                if (pulledToX < 0) {
+                    mEdgeGlowLeft.onPull((float) mOverScrollDeltaX / mHostView.getWidth());
+                    if (!mEdgeGlowRight.isFinished()) {
+                        mEdgeGlowRight.onRelease();
+                    }
+                } else if (pulledToX > maxX) {
+                    mEdgeGlowRight.onPull((float) mOverScrollDeltaX / mHostView.getWidth());
+                    if (!mEdgeGlowLeft.isFinished()) {
+                        mEdgeGlowLeft.onRelease();
+                    }
+                }
+                mOverScrollDeltaX = 0;
+            }
+
+            if (maxY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
+                final int pulledToY = oldY + mOverScrollDeltaY;
+                if (pulledToY < 0) {
+                    mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
+                    if (!mEdgeGlowBottom.isFinished()) {
+                        mEdgeGlowBottom.onRelease();
+                    }
+                } else if (pulledToY > maxY) {
+                    mEdgeGlowBottom.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
+                    if (!mEdgeGlowTop.isFinished()) {
+                        mEdgeGlowTop.onRelease();
+                    }
+                }
+                mOverScrollDeltaY = 0;
+            }
+        }
+    }
+
+    /**
+     * Set touch delta values indicating the current amount of overscroll.
+     *
+     * @param deltaX
+     * @param deltaY
+     */
+    public void setOverScrollDeltas(int deltaX, int deltaY) {
+        mOverScrollDeltaX = deltaX;
+        mOverScrollDeltaY = deltaY;
+    }
+
+    /**
+     * Absorb leftover fling velocity into one of the edge glows as appropriate.
+     *
+     * @param x Current X scroll offset
+     * @param y Current Y scroll offset
+     * @param oldX Old X scroll offset
+     * @param oldY Old Y scroll offset
+     * @param rangeX Maximum range for horizontal scrolling
+     * @param rangeY Maximum range for vertical scrolling
+     */
+    public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY) {
+        if (rangeY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
+            if (y < 0 && oldY >= 0) {
+                mEdgeGlowTop.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
+                if (!mEdgeGlowBottom.isFinished()) {
+                    mEdgeGlowBottom.onRelease();
+                }
+            } else if (y > rangeY && oldY <= rangeY) {
+                mEdgeGlowBottom.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
+                if (!mEdgeGlowTop.isFinished()) {
+                    mEdgeGlowTop.onRelease();
+                }
+            }
+        }
+
+        if (rangeX > 0) {
+            if (x < 0 && oldX >= 0) {
+                mEdgeGlowLeft.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
+                if (!mEdgeGlowRight.isFinished()) {
+                    mEdgeGlowRight.onRelease();
+                }
+            } else if (x > rangeX && oldX <= rangeX) {
+                mEdgeGlowRight.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
+                if (!mEdgeGlowLeft.isFinished()) {
+                    mEdgeGlowLeft.onRelease();
+                }
+            }
+        }
+    }
+
+    /**
+     * Draw the glow effect along the sides of the widget. mEdgeGlow* must be non-null.
+     *
+     * @param canvas Canvas to draw into, transformed into view coordinates.
+     * @return true if glow effects are still animating and the view should invalidate again.
+     */
+    public boolean drawEdgeGlows(Canvas canvas) {
+        final int scrollX = mHostView.getScrollX();
+        final int scrollY = mHostView.getScrollY();
+        final int width = mHostView.getWidth();
+        int height = mHostView.getHeight();
+
+        boolean invalidateForGlow = false;
+        if (!mEdgeGlowTop.isFinished()) {
+            final int restoreCount = canvas.save();
+
+            canvas.translate(-width / 2 + scrollX, Math.min(0, scrollY));
+            mEdgeGlowTop.setSize(width * 2, height);
+            invalidateForGlow |= mEdgeGlowTop.draw(canvas);
+            canvas.restoreToCount(restoreCount);
+        }
+        if (!mEdgeGlowBottom.isFinished()) {
+            final int restoreCount = canvas.save();
+
+            canvas.translate(-width / 2 + scrollX,
+                    Math.max(mHostView.computeMaxScrollY(), scrollY) + height);
+            canvas.rotate(180, width, 0);
+            mEdgeGlowBottom.setSize(width * 2, height);
+            invalidateForGlow |= mEdgeGlowBottom.draw(canvas);
+            canvas.restoreToCount(restoreCount);
+        }
+        if (!mEdgeGlowLeft.isFinished()) {
+            final int restoreCount = canvas.save();
+
+            canvas.rotate(270);
+            canvas.translate(-height * 1.5f - scrollY, Math.min(0, scrollX));
+            mEdgeGlowLeft.setSize(height * 2, width);
+            invalidateForGlow |= mEdgeGlowLeft.draw(canvas);
+            canvas.restoreToCount(restoreCount);
+        }
+        if (!mEdgeGlowRight.isFinished()) {
+            final int restoreCount = canvas.save();
+
+            canvas.rotate(90);
+            canvas.translate(-height / 2 + scrollY,
+                    -(Math.max(mHostView.computeMaxScrollX(), scrollX) + width));
+            mEdgeGlowRight.setSize(height * 2, width);
+            invalidateForGlow |= mEdgeGlowRight.draw(canvas);
+            canvas.restoreToCount(restoreCount);
+        }
+        return invalidateForGlow;
+    }
+
+    /**
+     * @return True if any glow is still animating
+     */
+    public boolean isAnimating() {
+        return (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished() ||
+                !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished());
+    }
+
+    /**
+     * Release all glows from any touch pulls in progress.
+     */
+    public void releaseAll() {
+        mEdgeGlowTop.onRelease();
+        mEdgeGlowBottom.onRelease();
+        mEdgeGlowLeft.onRelease();
+        mEdgeGlowRight.onRelease();
+    }
+}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index b2ba7e23..755366c 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -330,4 +330,14 @@
      */
     public void setInstallableWebApp() { }
 
+    /**
+     * Tell the client that the page being viewed has an autofillable
+     * form and the user would like to set a profile up.
+     * @param msg A Message to send once the user has successfully
+     *      set up a profile and to inform the WebTextView it should
+     *      now autofill using that new profile.
+     * @hide
+     */
+    public void setupAutoFill(Message msg) { }
+
 }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index f4caa74..2e69d99 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -268,6 +268,8 @@
 
     private AutoFillProfile mAutoFillProfile;
 
+    private boolean         mUseWebViewBackgroundForOverscroll = true;
+
     // private WebSettings, not accessible by the host activity
     static private int      mDoubleTapToastCount = 3;
 
@@ -631,6 +633,23 @@
     }
 
     /**
+     * Set whether the WebView uses its background for over scroll background.
+     * If true, it will use the WebView's background. If false, it will use an
+     * internal pattern. Default is true.
+     */
+    public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
+        mUseWebViewBackgroundForOverscroll = view;
+    }
+
+    /**
+     * Returns true if this WebView uses WebView's background instead of
+     * internal pattern for over scroll background.
+     */
+    public boolean getUseWebViewBackgroundForOverscrollBackground() {
+        return mUseWebViewBackgroundForOverscroll;
+    }
+
+    /**
      * Store whether the WebView is saving form data.
      */
     public void setSaveFormData(boolean save) {
@@ -1626,6 +1645,13 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    public synchronized AutoFillProfile getAutoFillProfile() {
+        return mAutoFillProfile;
+    }
+
     int getDoubleTapToastCount() {
         return mDoubleTapToastCount;
     }
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 1caa707..e1a5c2d 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -26,9 +26,14 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.text.BoringLayout.Metrics;
+import android.text.DynamicLayout;
 import android.text.Editable;
 import android.text.InputFilter;
 import android.text.Layout;
+import android.text.Layout.Alignment;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.TextPaint;
@@ -126,6 +131,7 @@
 
     private boolean mAutoFillable; // Is this textview part of an autofillable form?
     private int mQueryId;
+    private boolean mAutoFillProfileIsSet;
 
     // Types used with setType.  Keep in sync with CachedInput.h
     private static final int NORMAL_TEXT_FIELD = 0;
@@ -137,6 +143,9 @@
     private static final int TELEPHONE = 6;
     private static final int URL = 7;
 
+    private static final int AUTOFILL_FORM = 100;
+    private Handler mHandler;
+
     /**
      * Create a new WebTextView.
      * @param   context The Context for this WebTextView.
@@ -160,6 +169,18 @@
         setTextColor(DebugFlags.DRAW_WEBTEXTVIEW ? Color.RED : Color.BLACK);
         // This helps to align the text better with the text in the web page.
         setIncludeFontPadding(false);
+
+        mHandler = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                case AUTOFILL_FORM:
+                    mWebView.autoFillForm(mQueryId);
+                    break;
+                }
+            }
+        };
+
     }
 
     public void setAutoFillable(int queryId) {
@@ -359,6 +380,68 @@
         }
     }
 
+    @Override
+    protected void makeNewLayout(int w, int hintWidth, Metrics boring,
+            Metrics hintBoring, int ellipsisWidth, boolean bringIntoView) {
+        // Necessary to get a Layout to work with, and to do the other work that
+        // makeNewLayout does.
+        super.makeNewLayout(w, hintWidth, boring, hintBoring, ellipsisWidth,
+                bringIntoView);
+
+        // For fields that do not draw, create a layout which is altered so that
+        // the text lines up.
+        if (DebugFlags.DRAW_WEBTEXTVIEW || willNotDraw()) {
+            float lineHeight = -1;
+            if (mWebView != null) {
+                float height = mWebView.nativeFocusCandidateLineHeight();
+                if (height != -1) {
+                    lineHeight = height * mWebView.getScale();
+                }
+            }
+            CharSequence text = getText();
+            // Copy from the existing Layout.
+            mLayout = new WebTextViewLayout(text, text, getPaint(), w,
+                    mLayout.getAlignment(), mLayout.getSpacingMultiplier(),
+                    mLayout.getSpacingAdd(), false, null, ellipsisWidth,
+                    lineHeight);
+        }
+    }
+
+    /**
+     * Custom layout which figures out its line spacing.  If -1 is passed in for
+     * the height, it will use the ascent and descent from the paint to
+     * determine the line spacing.  Otherwise it will use the spacing provided.
+     */
+    private static class WebTextViewLayout extends DynamicLayout {
+        private float mLineHeight;
+        private float mDifference;
+        public WebTextViewLayout(CharSequence base, CharSequence display,
+                TextPaint paint,
+                int width, Alignment align,
+                float spacingMult, float spacingAdd,
+                boolean includepad,
+                TextUtils.TruncateAt ellipsize, int ellipsizedWidth,
+                float lineHeight) {
+            super(base, display, paint, width, align, spacingMult, spacingAdd,
+                    includepad, ellipsize, ellipsizedWidth);
+            float paintLineHeight = paint.descent() - paint.ascent();
+            if (lineHeight == -1f) {
+                mLineHeight = paintLineHeight;
+                mDifference = 0f;
+            } else {
+                mLineHeight = lineHeight;
+                // Through trial and error, I found this calculation to improve
+                // the accuracy of line placement.
+                mDifference = (lineHeight - paintLineHeight) / 2;
+            }
+        }
+
+        @Override
+        public int getLineTop(int line) {
+            return Math.round(mLineHeight * line - mDifference);
+        }
+    }
+
     @Override public InputConnection onCreateInputConnection(
             EditorInfo outAttrs) {
         InputConnection connection = super.onCreateInputConnection(outAttrs);
@@ -736,8 +819,17 @@
                     if (id == 0 && position == 0) {
                         // Blank out the text box while we wait for WebCore to fill the form.
                         replaceText("");
-                        // Call a webview method to tell WebCore to autofill the form.
-                        mWebView.autoFillForm(mQueryId);
+                        WebSettings settings = mWebView.getSettings();
+                        if (mAutoFillProfileIsSet) {
+                            // Call a webview method to tell WebCore to autofill the form.
+                            mWebView.autoFillForm(mQueryId);
+                        } else {
+                            // There is no autofill profile setup yet and the user has
+                            // elected to try and set one up. Call through to the
+                            // embedder to action that.
+                            mWebView.getWebChromeClient().setupAutoFill(
+                                    mHandler.obtainMessage(AUTOFILL_FORM));
+                        }
                     }
                 }
             });
@@ -1059,4 +1151,8 @@
     /* package */ void updateCachedTextfield() {
         mWebView.updateCachedTextfield(getText().toString());
     }
+
+    /* package */ void setAutoFillProfileIsSet(boolean autoFillProfileIsSet) {
+        mAutoFillProfileIsSet = autoFillProfileIsSet;
+    }
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5bf2ad4..9b519c2 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,19 +16,25 @@
 
 package android.webkit;
 
+import com.android.internal.R;
+
 import android.annotation.Widget;
 import android.app.AlertDialog;
 import android.content.BroadcastReceiver;
 import android.content.ClipboardManager;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.IntentFilter;
 import android.content.DialogInterface.OnCancelListener;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.CornerPathEffect;
@@ -40,6 +46,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
+import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.net.http.SslCertificate;
@@ -81,13 +88,12 @@
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.CheckedTextView;
+import android.widget.EdgeGlow;
 import android.widget.LinearLayout;
 import android.widget.ListView;
-import android.widget.Scroller;
+import android.widget.OverScroller;
 import android.widget.Toast;
 
-import junit.framework.Assert;
-
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -102,6 +108,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import junit.framework.Assert;
+
 /**
  * <p>A View that displays web pages. This class is the basis upon which you
  * can roll your own web browser or simply display some online content within your Activity.
@@ -529,7 +537,13 @@
     // time for the longest scroll animation
     private static final int MAX_DURATION = 750;   // milliseconds
     private static final int SLIDE_TITLE_DURATION = 500;   // milliseconds
-    private Scroller mScroller;
+
+    // Used by OverScrollGlow
+    OverScroller mScroller;
+
+    private boolean mInOverScrollMode = false;
+    private static Paint mOverScrollBackground;
+    private static Paint mOverScrollBorder;
 
     private boolean mWrapContent;
     private static final int MOTIONLESS_FALSE           = 0;
@@ -734,6 +748,20 @@
     // variable to cache the above pattern in case accessibility is enabled.
     private Pattern mMatchAxsUrlParameterPattern;
 
+    /**
+     * Max distance to overscroll by in pixels.
+     * This how far content can be pulled beyond its normal bounds by the user.
+     */
+    private int mOverscrollDistance;
+
+    /**
+     * Max distance to overfling by in pixels.
+     * This is how far flinged content can move beyond the end of its normal bounds.
+     */
+    private int mOverflingDistance;
+
+    private OverScrollGlow mOverScrollGlow;
+
     // Used to match key downs and key ups
     private boolean mGotKeyDown;
 
@@ -906,9 +934,10 @@
 
         mCallbackProxy = new CallbackProxy(context, this);
         mViewManager = new ViewManager(this);
+        L10nUtils.loadStrings(context);
         mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
         mDatabase = WebViewDatabase.getInstance(context);
-        mScroller = new Scroller(context);
+        mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel
         mZoomManager = new ZoomManager(this, mCallbackProxy);
 
         /* The init method must follow the creation of certain member variables,
@@ -922,7 +951,6 @@
             startPrivateBrowsing();
         }
 
-        L10nUtils.loadStrings(context);
         mAutoFillData = new WebViewCore.AutoFillData();
     }
 
@@ -1044,6 +1072,9 @@
 
         // Compute the inverse of the density squared.
         DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
+
+        mOverscrollDistance = configuration.getScaledOverscrollDistance();
+        mOverflingDistance = configuration.getScaledOverflingDistance();
     }
 
     /**
@@ -1066,6 +1097,18 @@
                 new TextToSpeech(getContext(), null));
     }
 
+    @Override
+    public void setOverScrollMode(int mode) {
+        super.setOverScrollMode(mode);
+        if (mode != OVER_SCROLL_NEVER) {
+            if (mOverScrollGlow == null) {
+                mOverScrollGlow = new OverScrollGlow(this);
+            }
+        } else {
+            mOverScrollGlow = null;
+        }
+    }
+
     /* package */void updateDefaultZoomDensity(int zoomDensity) {
         final float density = mContext.getResources().getDisplayMetrics().density
                 * 100 / zoomDensity;
@@ -1197,7 +1240,8 @@
      * @hide
      */
     public int getVisibleTitleHeight() {
-        return Math.max(getTitleHeight() - mScrollY, 0);
+        // need to restrict mScrollY due to over scroll
+        return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
     }
 
     /*
@@ -1946,7 +1990,7 @@
         }
         nativeClearCursor(); // start next trackball movement from page edge
         if (bottom) {
-            return pinScrollTo(mScrollX, computeVerticalScrollRange(), true, 0);
+            return pinScrollTo(mScrollX, computeRealVerticalScrollRange(), true, 0);
         }
         // Page down.
         int h = getHeight();
@@ -2171,13 +2215,15 @@
 
     // Expects x in view coordinates
     int pinLocX(int x) {
-        return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
+        if (mInOverScrollMode) return x;
+        return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
     }
 
     // Expects y in view coordinates
     int pinLocY(int y) {
+        if (mInOverScrollMode) return y;
         return pinLoc(y, getViewHeightWithTitle(),
-                      computeVerticalScrollRange() + getTitleHeight());
+                      computeRealVerticalScrollRange() + getTitleHeight());
     }
 
     /**
@@ -2412,7 +2458,7 @@
     // Sets r to be our visible rectangle in content coordinates
     private void calcOurContentVisibleRect(Rect r) {
         calcOurVisibleRect(r);
-        // pin the rect to the bounds of the content
+        // since we might overscroll, pin the rect to the bounds of the content
         r.left = Math.max(viewToContentX(r.left), 0);
         // viewToContentY will remove the total height of the title bar.  Add
         // the visible height back in to account for the fact that if the title
@@ -2497,8 +2543,7 @@
         return false;
     }
 
-    @Override
-    protected int computeHorizontalScrollRange() {
+    private int computeRealHorizontalScrollRange() {
         if (mDrawHistory) {
             return mHistoryWidth;
         } else if (mHorizontalScrollBarMode == SCROLLBAR_ALWAYSOFF
@@ -2512,7 +2557,27 @@
     }
 
     @Override
-    protected int computeVerticalScrollRange() {
+    protected int computeHorizontalScrollRange() {
+        int range = computeRealHorizontalScrollRange();
+
+        // Adjust reported range if overscrolled to compress the scroll bars
+        final int scrollX = mScrollX;
+        final int overscrollRight = computeMaxScrollX();
+        if (scrollX < 0) {
+            range -= scrollX;
+        } else if (scrollX > overscrollRight) {
+            range += scrollX - overscrollRight;
+        }
+
+        return range;
+    }
+
+    @Override
+    protected int computeHorizontalScrollOffset() {
+        return Math.max(mScrollX, 0);
+    }
+
+    private int computeRealVerticalScrollRange() {
         if (mDrawHistory) {
             return mHistoryHeight;
         } else if (mVerticalScrollBarMode == SCROLLBAR_ALWAYSOFF
@@ -2526,6 +2591,22 @@
     }
 
     @Override
+    protected int computeVerticalScrollRange() {
+        int range = computeRealVerticalScrollRange();
+
+        // Adjust reported range if overscrolled to compress the scroll bars
+        final int scrollY = mScrollY;
+        final int overscrollBottom = computeMaxScrollY();
+        if (scrollY < 0) {
+            range -= scrollY;
+        } else if (scrollY > overscrollBottom) {
+            range += scrollY - overscrollBottom;
+        }
+
+        return range;
+    }
+
+    @Override
     protected int computeVerticalScrollOffset() {
         return Math.max(mScrollY - getTitleHeight(), 0);
     }
@@ -2540,10 +2621,39 @@
     protected void onDrawVerticalScrollBar(Canvas canvas,
                                            Drawable scrollBar,
                                            int l, int t, int r, int b) {
+        if (mScrollY < 0) {
+            t -= mScrollY;
+        }
         scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b);
         scrollBar.draw(canvas);
     }
 
+    @Override
+    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
+            boolean clampedY) {
+        mInOverScrollMode = false;
+        int maxX = computeMaxScrollX();
+        int maxY = computeMaxScrollY();
+        if (maxX == 0) {
+            // do not over scroll x if the page just fits the screen
+            scrollX = pinLocX(scrollX);
+        } else if (scrollX < 0 || scrollX > maxX) {
+            mInOverScrollMode = true;
+        }
+        if (scrollY < 0 || scrollY > maxY) {
+            mInOverScrollMode = true;
+        }
+
+        int oldX = mScrollX;
+        int oldY = mScrollY;
+
+        super.scrollTo(scrollX, scrollY);
+
+        if (mOverScrollGlow != null) {
+            mOverScrollGlow.pullGlow(mScrollX, mScrollY, oldX, oldY, maxX, maxY);
+        }
+    }
+
     /**
      * Get the url for the current page. This is not always the same as the url
      * passed to WebViewClient.onPageStarted because although the load for
@@ -2923,17 +3033,29 @@
         if (mScroller.computeScrollOffset()) {
             int oldX = mScrollX;
             int oldY = mScrollY;
-            mScrollX = mScroller.getCurrX();
-            mScrollY = mScroller.getCurrY();
-            postInvalidate();  // So we draw again
-            if (oldX != mScrollX || oldY != mScrollY) {
-                onScrollChanged(mScrollX, mScrollY, oldX, oldY);
-            } else if (mScroller.getStartX() != mScrollX
-                    || mScroller.getStartY() != mScrollY) {
-                abortAnimation();
-                mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
-                WebViewCore.resumePriority();
-                WebViewCore.resumeUpdatePicture(mWebViewCore);
+            int x = mScroller.getCurrX();
+            int y = mScroller.getCurrY();
+            invalidate();  // So we draw again
+
+            if (!mScroller.isFinished()) {
+                final int rangeX = computeMaxScrollX();
+                final int rangeY = computeMaxScrollY();
+                overScrollBy(x - oldX, y - oldY, oldX, oldY,
+                        rangeX, rangeY,
+                        mOverflingDistance, mOverflingDistance, false);
+
+                if (mOverScrollGlow != null) {
+                    mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY);
+                }
+            } else {
+                mScrollX = x;
+                mScrollY = y;
+                if (mScroller.getStartX() != mScrollX || mScroller.getStartY() != mScrollY) {
+                    abortAnimation();
+                    mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
+                    WebViewCore.resumePriority();
+                    WebViewCore.resumeUpdatePicture(mWebViewCore);
+                }
             }
         } else {
             super.computeScroll();
@@ -3436,6 +3558,40 @@
         drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
     }
 
+    /**
+     * Draw the background when beyond bounds
+     * @param canvas Canvas to draw into
+     */
+    private void drawOverScrollBackground(Canvas canvas) {
+        if (mOverScrollBackground == null) {
+            mOverScrollBackground = new Paint();
+            Bitmap bm = BitmapFactory.decodeResource(
+                    mContext.getResources(),
+                    com.android.internal.R.drawable.status_bar_background);
+            mOverScrollBackground.setShader(new BitmapShader(bm,
+                    Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
+            mOverScrollBorder = new Paint();
+            mOverScrollBorder.setStyle(Paint.Style.STROKE);
+            mOverScrollBorder.setStrokeWidth(0);
+            mOverScrollBorder.setColor(0xffbbbbbb);
+        }
+
+        int top = 0;
+        int right = computeRealHorizontalScrollRange();
+        int bottom = top + computeRealVerticalScrollRange();
+        // first draw the background and anchor to the top of the view
+        canvas.save();
+        canvas.translate(mScrollX, mScrollY);
+        canvas.clipRect(-mScrollX, top - mScrollY, right - mScrollX, bottom
+                - mScrollY, Region.Op.DIFFERENCE);
+        canvas.drawPaint(mOverScrollBackground);
+        canvas.restore();
+        // then draw the border
+        canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
+        // next clip the region for the content
+        canvas.clipRect(0, top, right, bottom);
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
@@ -3452,6 +3608,10 @@
         }
 
         int saveCount = canvas.save();
+        if (mInOverScrollMode && !getSettings()
+                .getUseWebViewBackgroundForOverscrollBackground()) {
+            drawOverScrollBackground(canvas);
+        }
         if (mTitleBar != null) {
             canvas.translate(0, (int) mTitleBar.getHeight());
         }
@@ -3466,6 +3626,10 @@
         }
         mWebViewCore.signalRepaintDone();
 
+        if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) {
+            invalidate();
+        }
+
         // paint the highlight in the end
         if (!mTouchHighlightRegion.isEmpty()) {
             if (mTouchHightlightPaint == null) {
@@ -3549,6 +3713,15 @@
          * click action, look for a word under the  click. If one is found,
          * animate the text selection into view.
          * FIXME: no animation code yet */
+        return selectText();
+    }
+
+    /**
+     * Select the word at the last click point.
+     *
+     * @hide pending API council approval
+     */
+    public boolean selectText() {
         int x = viewToContentX((int) mLastTouchX + mScrollX);
         int y = viewToContentY((int) mLastTouchY + mScrollY);
         setUpSelect();
@@ -3561,6 +3734,16 @@
         return false;
     }
 
+    private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        if (mSelectingText && mOrientation != newConfig.orientation) {
+            selectionDone();
+        }
+        mOrientation = newConfig.orientation;
+    }
+
     /**
      * Keep track of the Callback so we can end its ActionMode or remove its
      * titlebar.
@@ -4042,10 +4225,20 @@
                 // Note that code inside the adapter click handler in WebTextView depends
                 // on the AutoFill item being at the top of the drop down list. If you change
                 // the order, make sure to do it there too!
-                pastEntries.add(getResources().getText(
-                        com.android.internal.R.string.autofill_this_form).toString() +
-                        " " +
-                        mAutoFillData.getPreviewString());
+                WebSettings settings = getSettings();
+                if (settings != null && settings.getAutoFillProfile() != null) {
+                    pastEntries.add(getResources().getText(
+                            com.android.internal.R.string.autofill_this_form).toString() +
+                            " " +
+                            mAutoFillData.getPreviewString());
+                    mWebTextView.setAutoFillProfileIsSet(true);
+                } else {
+                    // There is no autofill profile set up yet, so add an option that
+                    // will invite the user to set their profile up.
+                    pastEntries.add(getResources().getText(
+                            com.android.internal.R.string.setup_autofill).toString());
+                    mWebTextView.setAutoFillProfileIsSet(false);
+                }
             }
 
             pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
@@ -4688,12 +4881,14 @@
     @Override
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
         super.onScrollChanged(l, t, oldl, oldt);
-        sendOurVisibleRect();
-        // update WebKit if visible title bar height changed. The logic is same
-        // as getVisibleTitleHeight.
-        int titleHeight = getTitleHeight();
-        if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
-            sendViewSizeZoom(false);
+        if (!mInOverScrollMode) {
+            sendOurVisibleRect();
+            // update WebKit if visible title bar height changed. The logic is same
+            // as getVisibleTitleHeight.
+            int titleHeight = getTitleHeight();
+            if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
+                sendViewSizeZoom(false);
+            }
         }
     }
 
@@ -4820,7 +5015,7 @@
 
         final ScaleGestureDetector detector =
                 mZoomManager.getMultiTouchGestureDetector();
-        boolean skipScaleGesture = false;
+        boolean isScrollGesture = false;
         // Set to the mid-point of a two-finger gesture used to detect if the
         // user has touched a layer.
         float gestureX = x;
@@ -4848,7 +5043,7 @@
                 }
                 action = ev.getActionMasked();
                 if (dist < DRAG_LAYER_FINGER_DISTANCE) {
-                    skipScaleGesture = true;
+                    isScrollGesture = true;
                 } else if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
                     // Fingers moved too far apart while dragging, the user
                     // might be trying to zoom.
@@ -4857,9 +5052,13 @@
             }
         }
 
-        // If the page disallows zoom, pass multi-pointer events to webkit.
-        if (!skipScaleGesture && ev.getPointerCount() > 1
-            && (mZoomManager.isZoomScaleFixed() || mDeferMultitouch)) {
+        // If the page disallows zoom, pass multi-touch events to webkit.
+        // mDeferMultitouch is a hack for layout tests, where it is used to
+        // force passing multi-touch events to webkit.
+        // FIXME: always pass multi-touch events to webkit and remove everything
+        // related to mDeferMultitouch.
+        if (ev.getPointerCount() > 1 &&
+                (mDeferMultitouch || (!isScrollGesture && mZoomManager.isZoomScaleFixed()))) {
             if (DebugFlags.WEB_VIEW) {
                 Log.v(LOGTAG, "passing " + ev.getPointerCount() + " points to webkit");
             }
@@ -4868,7 +5067,7 @@
         }
 
         if (mZoomManager.supportsMultiTouchZoom() && ev.getPointerCount() > 1 &&
-                mTouchMode != TOUCH_DRAG_LAYER_MODE && !skipScaleGesture) {
+                mTouchMode != TOUCH_DRAG_LAYER_MODE && !isScrollGesture) {
             if (!detector.isInProgress() &&
                     ev.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN) {
                 // Insert a fake pointer down event in order to start
@@ -5141,27 +5340,13 @@
                     deltaX = 0;
                     deltaY = 0;
 
-                    if (skipScaleGesture) {
+                    if (isScrollGesture) {
                         startScrollingLayer(gestureX, gestureY);
                     }
                     startDrag();
                 }
 
                 // do pan
-                if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-                    int newScrollX = pinLocX(mScrollX + deltaX);
-                    int newDeltaX = newScrollX - mScrollX;
-                    if (deltaX != newDeltaX) {
-                        deltaX = newDeltaX;
-                        fDeltaX = (float) newDeltaX;
-                    }
-                    int newScrollY = pinLocY(mScrollY + deltaY);
-                    int newDeltaY = newScrollY - mScrollY;
-                    if (deltaY != newDeltaY) {
-                        deltaY = newDeltaY;
-                        fDeltaY = (float) newDeltaY;
-                    }
-                }
                 boolean done = false;
                 boolean keepScrollBarsVisible = false;
                 if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
@@ -5351,6 +5536,12 @@
                             mHeldMotionless = MOTIONLESS_IGNORE;
                             doFling();
                             break;
+                        } else {
+                            if (mScroller.springBack(mScrollX, mScrollY, 0,
+                                    computeMaxScrollX(), 0,
+                                    computeMaxScrollY())) {
+                                invalidate();
+                            }
                         }
                         // redraw in high-quality, as we're done dragging
                         mHeldMotionless = MOTIONLESS_TRUE;
@@ -5371,6 +5562,8 @@
             }
             case MotionEvent.ACTION_CANCEL: {
                 if (mTouchMode == TOUCH_DRAG_MODE) {
+                    mScroller.springBack(mScrollX, mScrollY, 0,
+                            computeMaxScrollX(), 0, computeMaxScrollY());
                     invalidate();
                 }
                 cancelWebCoreTouchEvent(contentX, contentY, false);
@@ -5446,7 +5639,22 @@
                 }
                 return;
             }
-            scrollBy(deltaX, deltaY);
+
+            final int oldX = mScrollX;
+            final int oldY = mScrollY;
+            final int rangeX = computeMaxScrollX();
+            final int rangeY = computeMaxScrollY();
+
+            if (mOverScrollGlow != null) {
+                mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
+            }
+
+            overScrollBy(deltaX, deltaY, oldX, oldY,
+                    rangeX, rangeY,
+                    mOverscrollDistance, mOverscrollDistance, true);
+            if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
+                invalidate();
+            }
         }
         mZoomManager.keepZoomPickerVisible();
     }
@@ -5459,6 +5667,11 @@
             mVelocityTracker.recycle();
             mVelocityTracker = null;
         }
+
+        // Release any pulled glows
+        if (mOverScrollGlow != null) {
+            mOverScrollGlow.releaseAll();
+        }
     }
 
     private void cancelTouch() {
@@ -5469,6 +5682,7 @@
             mVelocityTracker.recycle();
             mVelocityTracker = null;
         }
+
         if (mTouchMode == TOUCH_DRAG_MODE ||
                 mTouchMode == TOUCH_DRAG_LAYER_MODE) {
             WebViewCore.resumePriority();
@@ -5780,12 +5994,20 @@
         }
     }
 
-    private int computeMaxScrollX() {
-        return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
+    /**
+     * Compute the maximum horizontal scroll position. Used by {@link OverScrollGlow}.
+     * @return Maximum horizontal scroll position within real content
+     */
+    int computeMaxScrollX() {
+        return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
     }
 
-    private int computeMaxScrollY() {
-        return Math.max(computeVerticalScrollRange() + getTitleHeight()
+    /**
+     * Compute the maximum vertical scroll position. Used by {@link OverScrollGlow}.
+     * @return Maximum vertical scroll position within real content
+     */
+    int computeMaxScrollY() {
+        return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
                 - getViewHeightWithTitle(), 0);
     }
 
@@ -5804,7 +6026,7 @@
 
     public void flingScroll(int vx, int vy) {
         mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
-                computeMaxScrollY());
+                computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
         invalidate();
     }
 
@@ -5834,6 +6056,10 @@
         if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
             WebViewCore.resumePriority();
             WebViewCore.resumeUpdatePicture(mWebViewCore);
+            if (mScroller.springBack(mScrollX, mScrollY, 0, computeMaxScrollX(),
+                    0, computeMaxScrollY())) {
+                invalidate();
+            }
             return;
         }
         float currentVelocity = mScroller.getCurrVelocity();
@@ -5860,13 +6086,37 @@
                     + " maxX=" + maxX + " maxY=" + maxY
                     + " mScrollX=" + mScrollX + " mScrollY=" + mScrollY);
         }
+
+        // Allow sloppy flings without overscrolling at the edges.
+        if ((mScrollX == 0 || mScrollX == maxX) && Math.abs(vx) < Math.abs(vy)) {
+            vx = 0;
+        }
+        if ((mScrollY == 0 || mScrollY == maxY) && Math.abs(vy) < Math.abs(vx)) {
+            vy = 0;
+        }
+
+        if (mOverscrollDistance < mOverflingDistance) {
+            if (mScrollX == -mOverscrollDistance || mScrollX == maxX + mOverscrollDistance) {
+                vx = 0;
+            }
+            if (mScrollY == -mOverscrollDistance || mScrollY == maxY + mOverscrollDistance) {
+                vy = 0;
+            }
+        }
+
         mLastVelX = vx;
         mLastVelY = vy;
         mLastVelocity = velocity;
 
-        mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
+        // no horizontal overscroll if the content just fits
+        mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
+                maxX == 0 ? 0 : mOverflingDistance, mOverflingDistance);
+        // Duration is calculated based on velocity. With range boundaries and overscroll
+        // we may not know how long the final animation will take. (Hence the deprecation
+        // warning on the call below.) It's not a big deal for scroll bars but if webcore
+        // resumes during this effect we will take a performance hit. See computeScroll;
+        // we resume webcore there when the animation is finished.
         final int time = mScroller.getDuration();
-        mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_PRIORITY, time);
         awakenScrollBars(time);
         invalidate();
     }
@@ -6665,6 +6915,10 @@
                             case MotionEvent.ACTION_CANCEL:
                                 if (mDeferTouchMode == TOUCH_DRAG_MODE) {
                                     // no fling in defer process
+                                    mScroller.springBack(mScrollX, mScrollY, 0,
+                                            computeMaxScrollX(), 0,
+                                            computeMaxScrollY());
+                                    invalidate();
                                     WebViewCore.resumePriority();
                                     WebViewCore.resumeUpdatePicture(mWebViewCore);
                                 }
@@ -7461,6 +7715,7 @@
     /* package */ native int      nativeFocusCandidatePointer();
     private native String   nativeFocusCandidateText();
     /* package */ native float    nativeFocusCandidateTextSize();
+    /* package */ native int nativeFocusCandidateLineHeight();
     /**
      * Returns an integer corresponding to WebView.cpp::type.
      * See WebTextView.setType()
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index a3e9bc9..423a788 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
@@ -39,6 +40,7 @@
 import android.util.SparseBooleanArray;
 import android.util.StateSet;
 import android.view.ActionMode;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
@@ -52,7 +54,6 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -138,6 +139,17 @@
     static final int TOUCH_MODE_FLING = 4;
 
     /**
+     * Indicates the touch gesture is an overscroll - a scroll beyond the beginning or end.
+     */
+    static final int TOUCH_MODE_OVERSCROLL = 5;
+
+    /**
+     * Indicates the view is being flung outside of normal content bounds
+     * and will spring back.
+     */
+    static final int TOUCH_MODE_OVERFLING = 6;
+
+    /**
      * Regular layout - usually an unsolicited layout from the view system
      */
     static final int LAYOUT_NORMAL = 0;
@@ -446,6 +458,16 @@
     private ContextMenuInfo mContextMenuInfo = null;
     
     /**
+     * Maximum distance to record overscroll
+     */
+    int mOverscrollMax;
+
+    /**
+     * Content height divided by this is the overscroll limit.
+     */
+    static final int OVERSCROLL_LIMIT_DIVISOR = 3;
+
+    /**
      * Used to request a layout when we changed touch mode
      */
     private static final int TOUCH_MODE_UNKNOWN = -1;
@@ -548,6 +570,48 @@
     private static final int INVALID_POINTER = -1;
 
     /**
+     * Maximum distance to overscroll by during edge effects
+     */
+    int mOverscrollDistance;
+
+    /**
+     * Maximum distance to overfling during edge effects
+     */
+    int mOverflingDistance;
+
+    // These two EdgeGlows are always set and used together.
+    // Checking one for null is as good as checking both.
+
+    /**
+     * Tracks the state of the top edge glow.
+     */
+    private EdgeGlow mEdgeGlowTop;
+
+    /**
+     * Tracks the state of the bottom edge glow.
+     */
+    private EdgeGlow mEdgeGlowBottom;
+
+    /**
+     * An estimate of how many pixels are between the top of the list and
+     * the top of the first position in the adapter, based on the last time
+     * we saw it. Used to hint where to draw edge glows.
+     */
+    private int mFirstPositionDistanceGuess;
+
+    /**
+     * An estimate of how many pixels are between the bottom of the list and
+     * the bottom of the last position in the adapter, based on the last time
+     * we saw it. Used to hint where to draw edge glows.
+     */
+    private int mLastPositionDistanceGuess;
+
+    /**
+     * Used for determining when to cancel out of overscroll.
+     */
+    private int mDirection = 0;
+
+    /**
      * Interface definition for a callback to be invoked when the list or grid
      * has been scrolled.
      */
@@ -690,9 +754,29 @@
         mTouchSlop = configuration.getScaledTouchSlop();
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+        mOverscrollDistance = configuration.getScaledOverscrollDistance();
+        mOverflingDistance = configuration.getScaledOverflingDistance();
+
         mDensityScale = getContext().getResources().getDisplayMetrics().density;
     }
 
+    @Override
+    public void setOverScrollMode(int mode) {
+        if (mode != OVER_SCROLL_NEVER) {
+            if (mEdgeGlowTop == null) {
+                final Resources res = getContext().getResources();
+                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
+                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
+                mEdgeGlowTop = new EdgeGlow(edge, glow);
+                mEdgeGlowBottom = new EdgeGlow(edge, glow);
+            }
+        } else {
+            mEdgeGlowTop = null;
+            mEdgeGlowBottom = null;
+        }
+        super.setOverScrollMode(mode);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -1003,6 +1087,18 @@
     }
 
     /**
+     * @return true if all list content currently fits within the view boundaries
+     */
+    private boolean contentFits() {
+        final int childCount = getChildCount();
+        if (childCount != mItemCount) {
+            return false;
+        }
+
+        return getChildAt(0).getTop() >= 0 && getChildAt(childCount - 1).getBottom() <= mBottom;
+    }
+
+    /**
      * Enables fast scrolling by letting the user quickly scroll through lists by
      * dragging the fast scroll thumb. The adapter attached to the list may want
      * to implement {@link SectionIndexer} if it wishes to display alphabet preview and
@@ -1540,6 +1636,10 @@
         int result;
         if (mSmoothScrollbarEnabled) {
             result = Math.max(mItemCount * 100, 0);
+            if (mScrollY != 0) {
+                // Compensate for overscroll
+                result += Math.abs((int) ((float) mScrollY / getHeight() * mItemCount * 100));
+            }
         } else {
             result = mItemCount;
         }
@@ -1612,6 +1712,8 @@
 
         layoutChildren();
         mInLayout = false;
+
+        mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
     }
 
     /**
@@ -2126,6 +2228,7 @@
                 mFlingRunnable.endFling();
                 if (mScrollY != 0) {
                     mScrollY = 0;
+                    finishGlows();
                     invalidate();
                 }
             }
@@ -2445,9 +2548,10 @@
         // Check if we have moved far enough that it looks more like a
         // scroll than a tap
         final int distance = Math.abs(deltaY);
-        if (distance > mTouchSlop) {
+        final boolean overscroll = mScrollY != 0;
+        if (overscroll || distance > mTouchSlop) {
             createScrollingCache();
-            mTouchMode = TOUCH_MODE_SCROLL;
+            mTouchMode = overscroll ? TOUCH_MODE_OVERSCROLL : TOUCH_MODE_SCROLL;
             mMotionCorrection = deltaY;
             final Handler handler = getHandler();
             // Handler should not be null unless the AbsListView is not attached to a
@@ -2483,6 +2587,19 @@
                 // touch mode). Force an initial layout to get rid of the selection.
                 layoutChildren();
             }
+        } else {
+            int touchMode = mTouchMode;
+            if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) {
+                if (mFlingRunnable != null) {
+                    mFlingRunnable.endFling();
+                }
+
+                if (mScrollY != 0) {
+                    mScrollY = 0;
+                    finishGlows();
+                    invalidate();
+                }
+            }
         }
     }
 
@@ -2513,50 +2630,63 @@
 
         switch (action & MotionEvent.ACTION_MASK) {
         case MotionEvent.ACTION_DOWN: {
-            mActivePointerId = ev.getPointerId(0);
-            final int x = (int) ev.getX();
-            final int y = (int) ev.getY();
-            int motionPosition = pointToPosition(x, y);
-            if (!mDataChanged) {
-                if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
-                        && (getAdapter().isEnabled(motionPosition))) {
-                    // User clicked on an actual view (and was not stopping a fling). It might be a
-                    // click or a scroll. Assume it is a click until proven otherwise
-                    mTouchMode = TOUCH_MODE_DOWN;
-                    // FIXME Debounce
-                    if (mPendingCheckForTap == null) {
-                        mPendingCheckForTap = new CheckForTap();
-                    }
-                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
-                } else {
-                    if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
-                        // If we couldn't find a view to click on, but the down event was touching
-                        // the edge, we will bail out and try again. This allows the edge correcting
-                        // code in ViewRoot to try to find a nearby view to select
-                        return false;
-                    }
+            switch (mTouchMode) {
+            case TOUCH_MODE_OVERFLING: {
+                mFlingRunnable.endFling();
+                mTouchMode = TOUCH_MODE_OVERSCROLL;
+                mMotionY = mLastY = (int) ev.getY();
+                mMotionCorrection = 0;
+                mActivePointerId = ev.getPointerId(0);
+                break;
+            }
 
-                    if (mTouchMode == TOUCH_MODE_FLING) {
-                        // Stopped a fling. It is a scroll.
-                        createScrollingCache();
-                        mTouchMode = TOUCH_MODE_SCROLL;
-                        mMotionCorrection = 0;
-                        motionPosition = findMotionRow(y);
-                        reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
-                        mFlingRunnable.flywheelTouch();
+            default: {
+                mActivePointerId = ev.getPointerId(0);
+                final int x = (int) ev.getX();
+                final int y = (int) ev.getY();
+                int motionPosition = pointToPosition(x, y);
+                if (!mDataChanged) {
+                    if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
+                            && (getAdapter().isEnabled(motionPosition))) {
+                        // User clicked on an actual view (and was not stopping a fling). It might be a
+                        // click or a scroll. Assume it is a click until proven otherwise
+                        mTouchMode = TOUCH_MODE_DOWN;
+                        // FIXME Debounce
+                        if (mPendingCheckForTap == null) {
+                            mPendingCheckForTap = new CheckForTap();
+                        }
+                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
+                    } else {
+                        if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
+                            // If we couldn't find a view to click on, but the down event was touching
+                            // the edge, we will bail out and try again. This allows the edge correcting
+                            // code in ViewRoot to try to find a nearby view to select
+                            return false;
+                        }
+
+                        if (mTouchMode == TOUCH_MODE_FLING) {
+                            // Stopped a fling. It is a scroll.
+                            createScrollingCache();
+                            mTouchMode = TOUCH_MODE_SCROLL;
+                            mMotionCorrection = 0;
+                            motionPosition = findMotionRow(y);
+                            mFlingRunnable.flywheelTouch();
+                        }
                     }
                 }
-            }
 
-            if (motionPosition >= 0) {
-                // Remember where the motion event started
-                v = getChildAt(motionPosition - mFirstPosition);
-                mMotionViewOriginalTop = v.getTop();
+                if (motionPosition >= 0) {
+                    // Remember where the motion event started
+                    v = getChildAt(motionPosition - mFirstPosition);
+                    mMotionViewOriginalTop = v.getTop();
+                }
+                mMotionX = x;
+                mMotionY = y;
+                mMotionPosition = motionPosition;
+                mLastY = Integer.MIN_VALUE;
+                break;
             }
-            mMotionX = x;
-            mMotionY = y;
-            mMotionPosition = motionPosition;
-            mLastY = Integer.MIN_VALUE;
+            }
             break;
         }
 
@@ -2594,9 +2724,25 @@
                         requestDisallowInterceptTouchEvent(true);
                     }
 
+                    final int rawDeltaY = deltaY;
                     deltaY -= mMotionCorrection;
                     int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
 
+                    final int motionIndex;
+                    if (mMotionPosition >= 0) {
+                        motionIndex = mMotionPosition - mFirstPosition;
+                    } else {
+                        // If we don't have a motion position that we can reliably track,
+                        // pick something in the middle to make a best guess at things below.
+                        motionIndex = getChildCount() / 2;
+                    }
+
+                    int motionViewPrevTop = 0;
+                    View motionView = this.getChildAt(motionIndex);
+                    if (motionView != null) {
+                        motionViewPrevTop = motionView.getTop();
+                    }
+
                     // No need to do all this work if we're not going to move anyway
                     boolean atEdge = false;
                     if (incrementalDeltaY != 0) {
@@ -2604,23 +2750,117 @@
                     }
 
                     // Check to see if we have bumped into the scroll limit
-                    if (atEdge && getChildCount() > 0) {
-                        // Treat this like we're starting a new scroll from the current
-                        // position. This will let the user start scrolling back into
-                        // content immediately rather than needing to scroll back to the
-                        // point where they hit the limit first.
-                        int motionPosition = findMotionRow(y);
-                        if (motionPosition >= 0) {
-                            final View motionView = getChildAt(motionPosition - mFirstPosition);
-                            mMotionViewOriginalTop = motionView.getTop();
+                    motionView = this.getChildAt(motionIndex);
+                    if (motionView != null) {
+                        // Check if the top of the motion view is where it is
+                        // supposed to be
+                        final int motionViewRealTop = motionView.getTop();
+                        if (atEdge) {
+                            // Apply overscroll
+
+                            int overscroll = -incrementalDeltaY -
+                                    (motionViewRealTop - motionViewPrevTop);
+                            overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
+                                    0, mOverscrollDistance, true);
+                            if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
+                                // Don't allow overfling if we're at the edge.
+                                mVelocityTracker.clear();
+                            }
+
+                            final int overscrollMode = getOverScrollMode();
+                            if (overscrollMode == OVER_SCROLL_ALWAYS ||
+                                    (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
+                                            !contentFits())) {
+                                mDirection = 0; // Reset when entering overscroll.
+                                mTouchMode = TOUCH_MODE_OVERSCROLL;
+                                if (rawDeltaY > 0) {
+                                    mEdgeGlowTop.onPull((float) overscroll / getHeight());
+                                    if (!mEdgeGlowBottom.isFinished()) {
+                                        mEdgeGlowBottom.onRelease();
+                                    }
+                                } else if (rawDeltaY < 0) {
+                                    mEdgeGlowBottom.onPull((float) overscroll / getHeight());
+                                    if (!mEdgeGlowTop.isFinished()) {
+                                        mEdgeGlowTop.onRelease();
+                                    }
+                                }
+                            }
                         }
                         mMotionY = y;
-                        mMotionPosition = motionPosition;
                         invalidate();
                     }
                     mLastY = y;
                 }
                 break;
+
+            case TOUCH_MODE_OVERSCROLL:
+                if (y != mLastY) {
+                    final int rawDeltaY = deltaY;
+                    deltaY -= mMotionCorrection;
+                    int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+
+                    final int oldScroll = mScrollY;
+                    final int newScroll = oldScroll - incrementalDeltaY;
+                    int newDirection = y > mLastY ? 1 : -1;
+
+                    if (mDirection == 0) {
+                        mDirection = newDirection;
+                    }
+
+                    if (mDirection != newDirection) {
+                        // Coming back to 'real' list scrolling
+                        incrementalDeltaY = -newScroll;
+                        mScrollY = 0;
+
+                        // No need to do all this work if we're not going to move anyway
+                        if (incrementalDeltaY != 0) {
+                            trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
+                        }
+
+                        // Check to see if we are back in
+                        View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
+                        if (motionView != null) {
+                            mTouchMode = TOUCH_MODE_SCROLL;
+
+                            // We did not scroll the full amount. Treat this essentially like the
+                            // start of a new touch scroll
+                            final int motionPosition = findClosestMotionRow(y);
+
+                            mMotionCorrection = 0;
+                            motionView = getChildAt(motionPosition - mFirstPosition);
+                            mMotionViewOriginalTop = motionView.getTop();
+                            mMotionY = y;
+                            mMotionPosition = motionPosition;
+                        }
+                    } else {
+                        overScrollBy(0, -incrementalDeltaY, 0, mScrollY, 0, 0,
+                                0, mOverscrollDistance, true);
+                        final int overscrollMode = getOverScrollMode();
+                        if (overscrollMode == OVER_SCROLL_ALWAYS ||
+                                (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
+                                        !contentFits())) {
+                            if (rawDeltaY > 0) {
+                                mEdgeGlowTop.onPull((float) -incrementalDeltaY / getHeight());
+                                if (!mEdgeGlowBottom.isFinished()) {
+                                    mEdgeGlowBottom.onRelease();
+                                }
+                            } else if (rawDeltaY < 0) {
+                                mEdgeGlowBottom.onPull((float) -incrementalDeltaY / getHeight());
+                                if (!mEdgeGlowTop.isFinished()) {
+                                    mEdgeGlowTop.onRelease();
+                                }
+                            }
+                            invalidate();
+                        }
+                        if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
+                            // Don't allow overfling if we're at the edge.
+                            mVelocityTracker.clear();
+                        }
+                    }
+                    mLastY = y;
+                    mDirection = newDirection;
+                }
+                break;
             }
 
             break;
@@ -2694,19 +2934,30 @@
             case TOUCH_MODE_SCROLL:
                 final int childCount = getChildCount();
                 if (childCount > 0) {
-                    if (mFirstPosition == 0 && getChildAt(0).getTop() >= mListPadding.top &&
+                    final int firstChildTop = getChildAt(0).getTop();
+                    final int lastChildBottom = getChildAt(childCount - 1).getBottom();
+                    final int contentTop = mListPadding.top;
+                    final int contentBottom = getHeight() - mListPadding.bottom;
+                    if (mFirstPosition == 0 && firstChildTop >= contentTop &&
                             mFirstPosition + childCount < mItemCount &&
-                            getChildAt(childCount - 1).getBottom() <=
-                                    getHeight() - mListPadding.bottom) {
+                            lastChildBottom <= getHeight() - contentBottom) {
                         mTouchMode = TOUCH_MODE_REST;
                         reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                     } else {
                         final VelocityTracker velocityTracker = mVelocityTracker;
                         velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+
                         final int initialVelocity = (int)
                                 (velocityTracker.getYVelocity(mActivePointerId) * mVelocityScale);
-    
-                        if (Math.abs(initialVelocity) > mMinimumVelocity) {
+                        // Fling if we have enough velocity and we aren't at a boundary.
+                        // Since we can potentially overfling more than we can overscroll, don't
+                        // allow the weird behavior where you can scroll to a boundary then
+                        // fling further.
+                        if (Math.abs(initialVelocity) > mMinimumVelocity &&
+                                !((mFirstPosition == 0 &&
+                                        firstChildTop == contentTop - mOverscrollDistance) ||
+                                  (mFirstPosition + childCount == mItemCount &&
+                                        lastChildBottom == contentBottom + mOverscrollDistance))) {
                             if (mFlingRunnable == null) {
                                 mFlingRunnable = new FlingRunnable();
                             }
@@ -2726,10 +2977,32 @@
                     reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                 }
                 break;
+
+            case TOUCH_MODE_OVERSCROLL:
+                if (mFlingRunnable == null) {
+                    mFlingRunnable = new FlingRunnable();
+                }
+                final VelocityTracker velocityTracker = mVelocityTracker;
+                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+
+                reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
+                if (Math.abs(initialVelocity) > mMinimumVelocity) {
+                    mFlingRunnable.startOverfling(-initialVelocity);
+                } else {
+                    mFlingRunnable.startSpringback();
+                }
+
+                break;
             }
 
             setPressed(false);
 
+            if (mEdgeGlowTop != null) {
+                mEdgeGlowTop.onRelease();
+                mEdgeGlowBottom.onRelease();
+            }
+
             // Need to redraw since we probably aren't drawing the selector anymore
             invalidate();
 
@@ -2760,24 +3033,42 @@
         }
 
         case MotionEvent.ACTION_CANCEL: {
-            mTouchMode = TOUCH_MODE_REST;
-            setPressed(false);
-            View motionView = getChildAt(mMotionPosition - mFirstPosition);
-            if (motionView != null) {
-                motionView.setPressed(false);
-            }
-            clearScrollingCache();
+            switch (mTouchMode) {
+            case TOUCH_MODE_OVERSCROLL:
+                if (mFlingRunnable == null) {
+                    mFlingRunnable = new FlingRunnable();
+                }
+                mFlingRunnable.startSpringback();
+                break;
 
-            final Handler handler = getHandler();
-            if (handler != null) {
-                handler.removeCallbacks(mPendingCheckForLongPress);
-            }
+            case TOUCH_MODE_OVERFLING:
+                // Do nothing - let it play out.
+                break;
 
-            if (mVelocityTracker != null) {
-                mVelocityTracker.recycle();
-                mVelocityTracker = null;
+            default:
+                mTouchMode = TOUCH_MODE_REST;
+                setPressed(false);
+                View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
+                if (motionView != null) {
+                    motionView.setPressed(false);
+                }
+                clearScrollingCache();
+
+                final Handler handler = getHandler();
+                if (handler != null) {
+                    handler.removeCallbacks(mPendingCheckForLongPress);
+                }
+
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
             }
             
+            if (mEdgeGlowTop != null) {
+                mEdgeGlowTop.onRelease();
+                mEdgeGlowBottom.onRelease();
+            }
             mActivePointerId = INVALID_POINTER;
             break;
         }
@@ -2802,10 +3093,61 @@
     }
 
     @Override
+    protected void onOverScrolled(int scrollX, int scrollY,
+            boolean clampedX, boolean clampedY) {
+        mScrollY = scrollY;
+
+        if (clampedY) {
+            // Velocity is broken by hitting the limit; don't start a fling off of this.
+            if (mVelocityTracker != null) {
+                mVelocityTracker.clear();
+            }
+        }
+        awakenScrollBars();
+    }
+
+    @Override
     public void draw(Canvas canvas) {
         super.draw(canvas);
+        if (mEdgeGlowTop != null) {
+            final int scrollY = mScrollY;
+            if (!mEdgeGlowTop.isFinished()) {
+                final int restoreCount = canvas.save();
+                final int width = getWidth();
+
+                canvas.translate(0, Math.min(0, scrollY + mFirstPositionDistanceGuess));
+                mEdgeGlowTop.setSize(width, getHeight());
+                if (mEdgeGlowTop.draw(canvas)) {
+                    invalidate();
+                }
+                canvas.restoreToCount(restoreCount);
+            }
+            if (!mEdgeGlowBottom.isFinished()) {
+                final int restoreCount = canvas.save();
+                final int width = getWidth();
+                final int height = getHeight();
+
+                canvas.translate(-width, 0);
+                canvas.rotate(-180, width, 0);
+                canvas.translate(0, -height);
+                mEdgeGlowBottom.setSize(width, height);
+                if (mEdgeGlowBottom.draw(canvas)) {
+                    invalidate();
+                }
+                canvas.restoreToCount(restoreCount);
+            }
+        }
         if (mFastScroller != null) {
-            mFastScroller.draw(canvas);
+            final int scrollY = mScrollY;
+            if (scrollY != 0) {
+                // Pin to the top/bottom during overscroll
+                int restoreCount = canvas.save();
+                canvas.translate(0, (float) scrollY);
+                mFastScroller.draw(canvas);
+                canvas.restoreToCount(restoreCount);
+            } else {
+                mFastScroller.draw(canvas);
+            }
         }
     }
 
@@ -2824,6 +3166,10 @@
         switch (action & MotionEvent.ACTION_MASK) {
         case MotionEvent.ACTION_DOWN: {
             int touchMode = mTouchMode;
+            if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) {
+                mMotionCorrection = 0;
+                return true;
+            }
             
             final int x = (int) ev.getX();
             final int y = (int) ev.getY();
@@ -2888,6 +3234,7 @@
             final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
             mMotionX = (int) ev.getX(newPointerIndex);
             mMotionY = (int) ev.getY(newPointerIndex);
+            mMotionCorrection = 0;
             mActivePointerId = ev.getPointerId(newPointerIndex);
             if (mVelocityTracker != null) {
                 mVelocityTracker.clear();
@@ -2943,7 +3290,7 @@
         /**
          * Tracks the decay of a fling scroll
          */
-        private final Scroller mScroller;
+        private final OverScroller mScroller;
 
         /**
          * Y value reported by mScroller on the previous fling
@@ -2954,7 +3301,7 @@
             public void run() {
                 final int activeId = mActivePointerId;
                 final VelocityTracker vt = mVelocityTracker;
-                final Scroller scroller = mScroller;
+                final OverScroller scroller = mScroller;
                 if (vt == null || activeId == INVALID_POINTER) {
                     return;
                 }
@@ -2968,6 +3315,7 @@
                 } else {
                     endFling();
                     mTouchMode = TOUCH_MODE_SCROLL;
+                    reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
                 }
             }
         };
@@ -2975,7 +3323,7 @@
         private static final int FLYWHEEL_TIMEOUT = 40; // milliseconds
 
         FlingRunnable() {
-            mScroller = new Scroller(getContext());
+            mScroller = new OverScroller(getContext());
         }
 
         void start(int initialVelocity) {
@@ -2998,6 +3346,42 @@
             }
         }
 
+        void startSpringback() {
+            if (mScroller.springBack(0, mScrollY, 0, 0, 0, 0)) {
+                mTouchMode = TOUCH_MODE_OVERFLING;
+                invalidate();
+                post(this);
+            } else {
+                mTouchMode = TOUCH_MODE_REST;
+            }
+        }
+
+        void startOverfling(int initialVelocity) {
+            final int min = mScrollY > 0 ? Integer.MIN_VALUE : 0;
+            final int max = mScrollY > 0 ? 0 : Integer.MAX_VALUE;
+            mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, min, max, 0, getHeight());
+            mTouchMode = TOUCH_MODE_OVERFLING;
+            invalidate();
+            post(this);
+        }
+
+        void edgeReached(int delta) {
+            mScroller.notifyVerticalEdgeReached(mScrollY, 0, mOverflingDistance);
+            final int overscrollMode = getOverScrollMode();
+            if (overscrollMode == OVER_SCROLL_ALWAYS ||
+                    (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) {
+                mTouchMode = TOUCH_MODE_OVERFLING;
+                final int vel = (int) mScroller.getCurrVelocity();
+                if (delta > 0) {
+                    mEdgeGlowTop.onAbsorb(vel);
+                } else {
+                    mEdgeGlowBottom.onAbsorb(vel);
+                }
+            }
+            invalidate();
+            post(this);
+        }
+
         void startScroll(int distance, int duration) {
             int initialY = distance < 0 ? Integer.MAX_VALUE : 0;
             mLastFlingY = initialY;
@@ -3040,58 +3424,100 @@
                     return;
                 }
                 // Fall through
-            case TOUCH_MODE_FLING:
+            case TOUCH_MODE_FLING: {
                 if (mItemCount == 0 || getChildCount() == 0) {
                     endFling();
                     return;
                 }
-                break;
-            }
-            final Scroller scroller = mScroller;
-            boolean more = scroller.computeScrollOffset();
-            final int y = scroller.getCurrY();
 
-            // Flip sign to convert finger direction to list items direction
-            // (e.g. finger moving down means list is moving towards the top)
-            int delta = mLastFlingY - y;
+                final OverScroller scroller = mScroller;
+                boolean more = scroller.computeScrollOffset();
+                final int y = scroller.getCurrY();
 
-            // Pretend that each frame of a fling scroll is a touch scroll
-            if (delta > 0) {
-                // List is moving towards the top. Use first view as mMotionPosition
-                mMotionPosition = mFirstPosition;
-                final View firstView = getChildAt(0);
-                mMotionViewOriginalTop = firstView.getTop();
+                // Flip sign to convert finger direction to list items direction
+                // (e.g. finger moving down means list is moving towards the top)
+                int delta = mLastFlingY - y;
 
-                // Don't fling more than 1 screen
-                delta = Math.min(getHeight() - mPaddingBottom - mPaddingTop - 1, delta);
-            } else {
-                // List is moving towards the bottom. Use last view as mMotionPosition
-                int offsetToLast = getChildCount() - 1;
-                mMotionPosition = mFirstPosition + offsetToLast;
+                // Pretend that each frame of a fling scroll is a touch scroll
+                if (delta > 0) {
+                    // List is moving towards the top. Use first view as mMotionPosition
+                    mMotionPosition = mFirstPosition;
+                    final View firstView = getChildAt(0);
+                    mMotionViewOriginalTop = firstView.getTop();
 
-                final View lastView = getChildAt(offsetToLast);
-                mMotionViewOriginalTop = lastView.getTop();
+                    // Don't fling more than 1 screen
+                    delta = Math.min(getHeight() - mPaddingBottom - mPaddingTop - 1, delta);
+                } else {
+                    // List is moving towards the bottom. Use last view as mMotionPosition
+                    int offsetToLast = getChildCount() - 1;
+                    mMotionPosition = mFirstPosition + offsetToLast;
 
-                // Don't fling more than 1 screen
-                delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta);
-            }
+                    final View lastView = getChildAt(offsetToLast);
+                    mMotionViewOriginalTop = lastView.getTop();
 
-            // Don't stop just because delta is zero (it could have been rounded)
-            final boolean atEnd = trackMotionScroll(delta, delta) && (delta != 0);
+                    // Don't fling more than 1 screen
+                    delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta);
+                }
 
-            if (more && !atEnd) {
-                invalidate();
-                mLastFlingY = y;
-                post(this);
-            } else {
-                endFling();
+                // Check to see if we have bumped into the scroll limit
+                View motionView = getChildAt(mMotionPosition - mFirstPosition);
+                int oldTop = 0;
+                if (motionView != null) {
+                    oldTop = motionView.getTop();
+                }
 
-                if (PROFILE_FLINGING) {
-                    if (mFlingProfilingStarted) {
-                        Debug.stopMethodTracing();
-                        mFlingProfilingStarted = false;
+                // Don't stop just because delta is zero (it could have been rounded)
+                final boolean atEnd = trackMotionScroll(delta, delta) && (delta != 0);
+                if (atEnd) {
+                    if (motionView != null) {
+                        // Tweak the scroll for how far we overshot
+                        int overshoot = -(delta - (motionView.getTop() - oldTop));
+                        overScrollBy(0, overshoot, 0, mScrollY, 0, 0,
+                                0, mOverflingDistance, false);
+                    }
+                    edgeReached(delta);
+                    break;
+                }
+
+                if (more && !atEnd) {
+                    invalidate();
+                    mLastFlingY = y;
+                    post(this);
+                } else {
+                    endFling();
+
+                    if (PROFILE_FLINGING) {
+                        if (mFlingProfilingStarted) {
+                            Debug.stopMethodTracing();
+                            mFlingProfilingStarted = false;
+                        }
+
+                        if (mFlingStrictSpan != null) {
+                            mFlingStrictSpan.finish();
+                            mFlingStrictSpan = null;
+                        }
                     }
                 }
+                break;
+            }
+
+            case TOUCH_MODE_OVERFLING: {
+                final OverScroller scroller = mScroller;
+                if (scroller.computeScrollOffset()) {
+                    final int scrollY = mScrollY;
+                    final int deltaY = scroller.getCurrY() - scrollY;
+                    if (overScrollBy(0, deltaY, 0, scrollY, 0, 0,
+                            0, mOverflingDistance, false)) {
+                        startSpringback();
+                    } else {
+                        invalidate();
+                        post(this);
+                    }
+                } else {
+                    endFling();
+                }
+                break;
+            }
             }
         }
     }
@@ -3620,16 +4046,29 @@
 
         final int firstPosition = mFirstPosition;
 
-        if (firstPosition == 0 && firstTop >= listPadding.top && deltaY >= 0) {
-            // Don't need to move views down if the top of the first position
-            // is already visible
-            return true;
+        // Update our guesses for where the first and last views are
+        if (firstPosition == 0) {
+            mFirstPositionDistanceGuess = firstTop - mListPadding.top;
+        } else {
+            mFirstPositionDistanceGuess += incrementalDeltaY;
+        }
+        if (firstPosition + childCount == mItemCount) {
+            mLastPositionDistanceGuess = lastBottom + mListPadding.bottom;
+        } else {
+            mLastPositionDistanceGuess += incrementalDeltaY;
         }
 
-        if (firstPosition + childCount == mItemCount && lastBottom <= end && deltaY <= 0) {
+        if (firstPosition == 0 && firstTop >= listPadding.top && incrementalDeltaY >= 0) {
+            // Don't need to move views down if the top of the first position
+            // is already visible
+            return incrementalDeltaY != 0;
+        }
+
+        if (firstPosition + childCount == mItemCount && lastBottom <= end &&
+                incrementalDeltaY <= 0) {
             // Don't need to move views up if the bottom of the last position
             // is already visible
-            return true;
+            return incrementalDeltaY != 0;
         }
 
         final boolean down = incrementalDeltaY < 0;
@@ -3805,6 +4244,22 @@
     abstract int findMotionRow(int y);
 
     /**
+     * Find the row closest to y. This row will be used as the motion row when scrolling.
+     *
+     * @param y Where the user touched
+     * @return The position of the first (or only) item in the row closest to y
+     */
+    int findClosestMotionRow(int y) {
+        final int childCount = getChildCount();
+        if (childCount == 0) {
+            return INVALID_POSITION;
+        }
+
+        final int motionRow = findMotionRow(y);
+        return motionRow != INVALID_POSITION ? motionRow : mFirstPosition + childCount - 1;
+    }
+
+    /**
      * Causes all the views to be rebuilt and redrawn.
      */
     public void invalidateViews() {
@@ -4577,6 +5032,13 @@
         return result;
     }
 
+    private void finishGlows() {
+        if (mEdgeGlowTop != null) {
+            mEdgeGlowTop.finish();
+            mEdgeGlowBottom.finish();
+        }
+    }
+
     /**
      * Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService
      * through the specified intent.
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
new file mode 100644
index 0000000..9b3a6e6
--- /dev/null
+++ b/core/java/android/widget/EdgeGlow.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+/**
+ * This class performs the glow effect used at the edges of scrollable widgets.
+ * @hide
+ */
+public class EdgeGlow {
+    private static final String TAG = "EdgeGlow";
+
+    // Time it will take the effect to fully recede in ms
+    private static final int RECEDE_TIME = 1000;
+
+    // Time it will take before a pulled glow begins receding
+    private static final int PULL_TIME = 167;
+
+    // Time it will take for a pulled glow to decay to partial strength before release
+    private static final int PULL_DECAY_TIME = 1000;
+
+    private static final float MAX_ALPHA = 0.8f;
+    private static final float HELD_EDGE_ALPHA = 0.7f;
+    private static final float HELD_EDGE_SCALE_Y = 0.5f;
+    private static final float HELD_GLOW_ALPHA = 0.5f;
+    private static final float HELD_GLOW_SCALE_Y = 0.5f;
+
+    private static final float MAX_GLOW_HEIGHT = 3.f;
+
+    private static final float PULL_GLOW_BEGIN = 1.f;
+    private static final float PULL_EDGE_BEGIN = 0.6f;
+
+    // Minimum velocity that will be absorbed
+    private static final int MIN_VELOCITY = 100;
+
+    private static final float EPSILON = 0.001f;
+
+    private final Drawable mEdge;
+    private final Drawable mGlow;
+    private int mWidth;
+    private int mHeight;
+
+    private float mEdgeAlpha;
+    private float mEdgeScaleY;
+    private float mGlowAlpha;
+    private float mGlowScaleY;
+
+    private float mEdgeAlphaStart;
+    private float mEdgeAlphaFinish;
+    private float mEdgeScaleYStart;
+    private float mEdgeScaleYFinish;
+    private float mGlowAlphaStart;
+    private float mGlowAlphaFinish;
+    private float mGlowScaleYStart;
+    private float mGlowScaleYFinish;
+
+    private long mStartTime;
+    private float mDuration;
+
+    private final Interpolator mInterpolator;
+
+    private static final int STATE_IDLE = 0;
+    private static final int STATE_PULL = 1;
+    private static final int STATE_ABSORB = 2;
+    private static final int STATE_RECEDE = 3;
+    private static final int STATE_PULL_DECAY = 4;
+
+    // How much dragging should effect the height of the edge image.
+    // Number determined by user testing.
+    private static final int PULL_DISTANCE_EDGE_FACTOR = 5;
+
+    // How much dragging should effect the height of the glow image.
+    // Number determined by user testing.
+    private static final int PULL_DISTANCE_GLOW_FACTOR = 5;
+    private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 0.8f;
+
+    private static final int VELOCITY_EDGE_FACTOR = 8;
+    private static final int VELOCITY_GLOW_FACTOR = 16;
+
+    private int mState = STATE_IDLE;
+
+    private float mPullDistance;
+
+    public EdgeGlow(Drawable edge, Drawable glow) {
+        mEdge = edge;
+        mGlow = glow;
+
+        mInterpolator = new DecelerateInterpolator();
+    }
+
+    public void setSize(int width, int height) {
+        mWidth = width;
+        mHeight = height;
+    }
+
+    public boolean isFinished() {
+        return mState == STATE_IDLE;
+    }
+
+    public void finish() {
+        mState = STATE_IDLE;
+    }
+
+    /**
+     * Call when the object is pulled by the user.
+     *
+     * @param deltaDistance Change in distance since the last call
+     */
+    public void onPull(float deltaDistance) {
+        final long now = AnimationUtils.currentAnimationTimeMillis();
+        if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
+            return;
+        }
+        if (mState != STATE_PULL) {
+            mGlowScaleY = PULL_GLOW_BEGIN;
+        }
+        mState = STATE_PULL;
+
+        mStartTime = now;
+        mDuration = PULL_TIME;
+
+        mPullDistance += deltaDistance;
+        float distance = Math.abs(mPullDistance);
+
+        mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, MAX_ALPHA));
+        mEdgeScaleY = mEdgeScaleYStart = Math.max(
+                HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f));
+
+        mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
+                mGlowAlpha +
+                (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
+
+        float glowChange = Math.abs(deltaDistance);
+        if (deltaDistance > 0 && mPullDistance < 0) {
+            glowChange = -glowChange;
+        }
+        if (mPullDistance == 0) {
+            mGlowScaleY = 0;
+        }
+
+        // Do not allow glow to get larger than MAX_GLOW_HEIGHT.
+        mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max(
+                0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR));
+
+        mEdgeAlphaFinish = mEdgeAlpha;
+        mEdgeScaleYFinish = mEdgeScaleY;
+        mGlowAlphaFinish = mGlowAlpha;
+        mGlowScaleYFinish = mGlowScaleY;
+    }
+
+    /**
+     * Call when the object is released after being pulled.
+     */
+    public void onRelease() {
+        mPullDistance = 0;
+
+        if (mState != STATE_PULL && mState != STATE_PULL_DECAY) {
+            return;
+        }
+
+        mState = STATE_RECEDE;
+        mEdgeAlphaStart = mEdgeAlpha;
+        mEdgeScaleYStart = mEdgeScaleY;
+        mGlowAlphaStart = mGlowAlpha;
+        mGlowScaleYStart = mGlowScaleY;
+
+        mEdgeAlphaFinish = 0.f;
+        mEdgeScaleYFinish = 0.f;
+        mGlowAlphaFinish = 0.f;
+        mGlowScaleYFinish = 0.f;
+
+        mStartTime = AnimationUtils.currentAnimationTimeMillis();
+        mDuration = RECEDE_TIME;
+    }
+
+    /**
+     * Call when the effect absorbs an impact at the given velocity.
+     *
+     * @param velocity Velocity at impact in pixels per second.
+     */
+    public void onAbsorb(int velocity) {
+        mState = STATE_ABSORB;
+        velocity = Math.max(MIN_VELOCITY, Math.abs(velocity));
+
+        mStartTime = AnimationUtils.currentAnimationTimeMillis();
+        mDuration = 0.1f + (velocity * 0.03f);
+
+        // The edge should always be at least partially visible, regardless
+        // of velocity.
+        mEdgeAlphaStart = 0.f;
+        mEdgeScaleY = mEdgeScaleYStart = 0.f;
+        // The glow depends more on the velocity, and therefore starts out
+        // nearly invisible.
+        mGlowAlphaStart = 0.5f;
+        mGlowScaleYStart = 0.f;
+
+        // Factor the velocity by 8. Testing on device shows this works best to
+        // reflect the strength of the user's scrolling.
+        mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1));
+        // Edge should never get larger than the size of its asset.
+        mEdgeScaleYFinish = Math.max(
+                HELD_EDGE_SCALE_Y, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1.f));
+
+        // Growth for the size of the glow should be quadratic to properly
+        // respond
+        // to a user's scrolling speed. The faster the scrolling speed, the more
+        // intense the effect should be for both the size and the saturation.
+        mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
+        // Alpha should change for the glow as well as size.
+        mGlowAlphaFinish = Math.max(
+                mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
+    }
+
+
+    /**
+     * Draw into the provided canvas. Assumes that the canvas has been rotated
+     * accordingly and the size has been set. The effect will be drawn the full
+     * width of X=0 to X=width, emitting from Y=0 and extending to some factor <
+     * 1.f of height.
+     *
+     * @param canvas Canvas to draw into
+     * @return true if drawing should continue beyond this frame to continue the
+     *         animation
+     */
+    public boolean draw(Canvas canvas) {
+        update();
+
+        final int edgeHeight = mEdge.getIntrinsicHeight();
+        final int edgeWidth = mEdge.getIntrinsicWidth();
+        final int glowHeight = mGlow.getIntrinsicHeight();
+        final int glowWidth = mGlow.getIntrinsicWidth();
+
+        mGlow.setAlpha((int) (Math.max(0, Math.min(mGlowAlpha, 1)) * 255));
+
+        // Center the glow inside the width of the container.
+        int glowLeft = (mWidth - glowWidth)/2;
+        mGlow.setBounds(glowLeft, 0, mWidth - glowLeft, (int) Math.min(
+                glowHeight * mGlowScaleY * glowHeight/ glowWidth * 0.6f,
+                glowHeight * MAX_GLOW_HEIGHT));
+        mGlow.draw(canvas);
+
+        mEdge.setAlpha((int) (Math.max(0, Math.min(mEdgeAlpha, 1)) * 255));
+
+        int edgeLeft = (mWidth - edgeWidth)/2;
+        mEdge.setBounds(edgeLeft, 0, mWidth - edgeLeft, (int) (edgeHeight * mEdgeScaleY));
+        mEdge.draw(canvas);
+
+        return mState != STATE_IDLE;
+    }
+
+    private void update() {
+        final long time = AnimationUtils.currentAnimationTimeMillis();
+        final float t = Math.min((time - mStartTime) / mDuration, 1.f);
+
+        final float interp = mInterpolator.getInterpolation(t);
+
+        mEdgeAlpha = mEdgeAlphaStart + (mEdgeAlphaFinish - mEdgeAlphaStart) * interp;
+        mEdgeScaleY = mEdgeScaleYStart + (mEdgeScaleYFinish - mEdgeScaleYStart) * interp;
+        mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
+        mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
+
+        if (t >= 1.f - EPSILON) {
+            switch (mState) {
+                case STATE_ABSORB:
+                    mState = STATE_RECEDE;
+                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
+                    mDuration = RECEDE_TIME;
+
+                    mEdgeAlphaStart = mEdgeAlpha;
+                    mEdgeScaleYStart = mEdgeScaleY;
+                    mGlowAlphaStart = mGlowAlpha;
+                    mGlowScaleYStart = mGlowScaleY;
+
+                    // After absorb, the glow and edge should fade to nothing.
+                    mEdgeAlphaFinish = 0.f;
+                    mEdgeScaleYFinish = 0.f;
+                    mGlowAlphaFinish = 0.f;
+                    mGlowScaleYFinish = 0.f;
+                    break;
+                case STATE_PULL:
+                    mState = STATE_PULL_DECAY;
+                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
+                    mDuration = PULL_DECAY_TIME;
+
+                    mEdgeAlphaStart = mEdgeAlpha;
+                    mEdgeScaleYStart = mEdgeScaleY;
+                    mGlowAlphaStart = mGlowAlpha;
+                    mGlowScaleYStart = mGlowScaleY;
+
+                    // After pull, the glow and edge should fade to nothing.
+                    mEdgeAlphaFinish = 0.f;
+                    mEdgeScaleYFinish = 0.f;
+                    mGlowAlphaFinish = 0.f;
+                    mGlowScaleYFinish = 0.f;
+                    break;
+                case STATE_PULL_DECAY:
+                    // When receding, we want edge to decrease more slowly
+                    // than the glow.
+                    float factor = mGlowScaleYFinish != 0 ? 1
+                            / (mGlowScaleYFinish * mGlowScaleYFinish)
+                            : Float.MAX_VALUE;
+                    mEdgeScaleY = mEdgeScaleYStart +
+                        (mEdgeScaleYFinish - mEdgeScaleYStart) *
+                            interp * factor;
+                    break;
+                case STATE_RECEDE:
+                    mState = STATE_IDLE;
+                    break;
+            }
+        }
+    }
+}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 114ae81..4146460 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1954,7 +1954,12 @@
         // TODO: Account for vertical spacing too
         final int numColumns = mNumColumns;
         final int rowCount = (mItemCount + numColumns - 1) / numColumns;
-        return Math.max(rowCount * 100, 0);
+        int result = Math.max(rowCount * 100, 0);
+        if (mScrollY != 0) {
+            // Compensate for overscroll
+            result += Math.abs((int) ((float) mScrollY / getHeight() * rowCount * 100));
+        }
+        return result;
     }
 }
 
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index e30d4c8..9fc91da 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -16,19 +16,24 @@
 
 package android.widget;
 
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
+import com.android.internal.R;
+
 import android.util.AttributeSet;
-import android.view.FocusFinder;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.view.View;
+import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.KeyEvent;
+import android.view.FocusFinder;
+import android.view.MotionEvent;
 import android.view.ViewParent;
 import android.view.animation.AnimationUtils;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 
 import java.util.List;
 
@@ -65,7 +70,9 @@
     private long mLastScroll;
 
     private final Rect mTempRect = new Rect();
-    private Scroller mScroller;
+    private OverScroller mScroller;
+    private EdgeGlow mEdgeGlowLeft;
+    private EdgeGlow mEdgeGlowRight;
 
     /**
      * Flag to indicate that we are moving focus ourselves. This is so the
@@ -119,6 +126,9 @@
     private int mMinimumVelocity;
     private int mMaximumVelocity;
     
+    private int mOverscrollDistance;
+    private int mOverflingDistance;
+
     /**
      * ID of the active pointer. This is used to retain consistency during
      * drags/flings if multiple pointers are used.
@@ -191,7 +201,7 @@
 
 
     private void initScrollView() {
-        mScroller = new Scroller(getContext());
+        mScroller = new OverScroller(getContext());
         setFocusable(true);
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
         setWillNotDraw(false);
@@ -199,6 +209,8 @@
         mTouchSlop = configuration.getScaledTouchSlop();
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+        mOverscrollDistance = configuration.getScaledOverscrollDistance();
+        mOverflingDistance = configuration.getScaledOverflingDistance();
     }
 
     @Override
@@ -463,6 +475,9 @@
                 /* Release the drag */
                 mIsBeingDragged = false;
                 mActivePointerId = INVALID_POINTER;
+                if (mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) {
+                    invalidate();
+                }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
@@ -495,9 +510,7 @@
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 final float x = ev.getX();
-                if (!(mIsBeingDragged = inChild((int) x, (int) ev.getY()))) {
-                    return false;
-                }
+                mIsBeingDragged = true;
 
                 /*
                  * If being flinged and user touches, stop the fling. isFinished
@@ -520,7 +533,36 @@
                     final int deltaX = (int) (mLastMotionX - x);
                     mLastMotionX = x;
 
-                    scrollBy(deltaX, 0);
+                    final int oldX = mScrollX;
+                    final int oldY = mScrollY;
+                    final int range = getScrollRange();
+                    if (overScrollBy(deltaX, 0, mScrollX, 0, range, 0,
+                            mOverscrollDistance, 0, true)) {
+                        // Break our velocity if we hit a scroll barrier.
+                        mVelocityTracker.clear();
+                    }
+                    onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+
+                    final int overscrollMode = getOverScrollMode();
+                    if (overscrollMode == OVER_SCROLL_ALWAYS ||
+                            (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
+                        final int pulledToX = oldX + deltaX;
+                        if (pulledToX < 0) {
+                            mEdgeGlowLeft.onPull((float) deltaX / getWidth());
+                            if (!mEdgeGlowRight.isFinished()) {
+                                mEdgeGlowRight.onRelease();
+                            }
+                        } else if (pulledToX > range) {
+                            mEdgeGlowRight.onPull((float) deltaX / getWidth());
+                            if (!mEdgeGlowLeft.isFinished()) {
+                                mEdgeGlowLeft.onRelease();
+                            }
+                        }
+                        if (mEdgeGlowLeft != null
+                                && (!mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished())) {
+                            invalidate();
+                        }
+                    }
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -529,8 +571,15 @@
                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                     int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
 
-                    if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) {
-                        fling(-initialVelocity);
+                    if (getChildCount() > 0) {
+                        if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+                            fling(-initialVelocity);
+                        } else {
+                            final int right = getScrollRange();
+                            if (mScroller.springBack(mScrollX, mScrollY, 0, right, 0, 0)) {
+                                invalidate();
+                            }
+                        }
                     }
                     
                     mActivePointerId = INVALID_POINTER;
@@ -540,16 +589,27 @@
                         mVelocityTracker.recycle();
                         mVelocityTracker = null;
                     }
+                    if (mEdgeGlowLeft != null) {
+                        mEdgeGlowLeft.onRelease();
+                        mEdgeGlowRight.onRelease();
+                    }
                 }
                 break;
             case MotionEvent.ACTION_CANCEL:
                 if (mIsBeingDragged && getChildCount() > 0) {
+                    if (mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) {
+                        invalidate();
+                    }
                     mActivePointerId = INVALID_POINTER;
                     mIsBeingDragged = false;
                     if (mVelocityTracker != null) {
                         mVelocityTracker.recycle();
                         mVelocityTracker = null;
                     }
+                    if (mEdgeGlowLeft != null) {
+                        mEdgeGlowLeft.onRelease();
+                        mEdgeGlowRight.onRelease();
+                    }
                 }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
@@ -576,12 +636,28 @@
         }
     }
     
+    @Override
+    protected void onOverScrolled(int scrollX, int scrollY,
+            boolean clampedX, boolean clampedY) {
+        // Treat animating scrolls differently; see #computeScroll() for why.
+        if (!mScroller.isFinished()) {
+            mScrollX = scrollX;
+            mScrollY = scrollY;
+            if (clampedX) {
+                mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0);
+            }
+        } else {
+            super.scrollTo(scrollX, scrollY);
+        }
+        awakenScrollBars();
+    }
+
     private int getScrollRange() {
         int scrollRange = 0;
         if (getChildCount() > 0) {
             View child = getChildAt(0);
             scrollRange = Math.max(0,
-                    child.getWidth() - getWidth() - mPaddingLeft - mPaddingRight);
+                    child.getWidth() - (getWidth() - mPaddingLeft - mPaddingRight));
         }
         return scrollRange;
     }
@@ -958,7 +1034,16 @@
             return contentWidth;
         }
         
-        return getChildAt(0).getRight();
+        int scrollRange = getChildAt(0).getRight();
+        final int scrollX = mScrollX;
+        final int overscrollRight = Math.max(0, scrollRange - contentWidth);
+        if (scrollX < 0) {
+            scrollRange -= scrollX;
+        } else if (scrollX > overscrollRight) {
+            scrollRange += scrollX - overscrollRight;
+        }
+
+        return scrollRange;
     }
     
     @Override
@@ -1019,14 +1104,20 @@
             int x = mScroller.getCurrX();
             int y = mScroller.getCurrY();
 
-            if (getChildCount() > 0) {
-                View child = getChildAt(0);
-                x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
-                y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
-                if (x != oldX || y != oldY) {
-                    mScrollX = x;
-                    mScrollY = y;
-                    onScrollChanged(x, y, oldX, oldY);
+            if (oldX != x || oldY != y) {
+                overScrollBy(x - oldX, y - oldY, oldX, oldY, getScrollRange(), 0,
+                        mOverflingDistance, 0, false);
+                onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+
+                final int range = getScrollRange();
+                final int overscrollMode = getOverScrollMode();
+                if (overscrollMode == OVER_SCROLL_ALWAYS ||
+                        (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
+                    if (x < 0 && oldX >= 0) {
+                        mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
+                    } else if (x > range && oldX <= range) {
+                        mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
+                    }
                 }
             }
             awakenScrollBars();
@@ -1263,7 +1354,7 @@
             int right = getChildAt(0).getWidth();
     
             mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0, 
-                    Math.max(0, right - width), 0, 0);
+                    Math.max(0, right - width), 0, 0, width/2, 0);
     
             final boolean movingRight = velocityX > 0;
     
@@ -1301,6 +1392,56 @@
         }
     }
 
+    @Override
+    public void setOverScrollMode(int mode) {
+        if (mode != OVER_SCROLL_NEVER) {
+            if (mEdgeGlowLeft == null) {
+                final Resources res = getContext().getResources();
+                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
+                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
+                mEdgeGlowLeft = new EdgeGlow(edge, glow);
+                mEdgeGlowRight = new EdgeGlow(edge, glow);
+            }
+        } else {
+            mEdgeGlowLeft = null;
+            mEdgeGlowRight = null;
+        }
+        super.setOverScrollMode(mode);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        if (mEdgeGlowLeft != null) {
+            final int scrollX = mScrollX;
+            if (!mEdgeGlowLeft.isFinished()) {
+                final int restoreCount = canvas.save();
+                final int height = getHeight();
+
+                canvas.rotate(270);
+                canvas.translate(-height * 1.5f, Math.min(0, scrollX));
+                mEdgeGlowLeft.setSize(getHeight() * 2, getWidth());
+                if (mEdgeGlowLeft.draw(canvas)) {
+                    invalidate();
+                }
+                canvas.restoreToCount(restoreCount);
+            }
+            if (!mEdgeGlowRight.isFinished()) {
+                final int restoreCount = canvas.save();
+                final int width = getWidth();
+                final int height = getHeight();
+
+                canvas.rotate(90);
+                canvas.translate(-height / 2, -(Math.max(getScrollRange(), scrollX) + width));
+                mEdgeGlowRight.setSize(height * 2, width);
+                if (mEdgeGlowRight.draw(canvas)) {
+                    invalidate();
+                }
+                canvas.restoreToCount(restoreCount);
+            }
+        }
+    }
+
     private int clamp(int n, int my, int child) {
         if (my >= child || n < 0) {
             return 0;
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 502cc38..fd4f950 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -104,6 +104,9 @@
     Drawable mDivider;
     int mDividerHeight;
     
+    Drawable mOverScrollHeader;
+    Drawable mOverScrollFooter;
+
     private boolean mIsCacheColorOpaque;
     private boolean mDividerIsOpaque;
 
@@ -152,6 +155,18 @@
             setDivider(d);
         }
         
+        final Drawable osHeader = a.getDrawable(
+                com.android.internal.R.styleable.ListView_overScrollHeader);
+        if (osHeader != null) {
+            setOverscrollHeader(osHeader);
+        }
+
+        final Drawable osFooter = a.getDrawable(
+                com.android.internal.R.styleable.ListView_overScrollFooter);
+        if (osFooter != null) {
+            setOverscrollFooter(osFooter);
+        }
+
         // Use the height specified, zero being the default
         final int dividerHeight = a.getDimensionPixelSize(
                 com.android.internal.R.styleable.ListView_dividerHeight, 0);
@@ -2962,14 +2977,52 @@
         }
         super.setCacheColorHint(color);
     }
-    
+
+    void drawOverscrollHeader(Canvas canvas, Drawable drawable, Rect bounds) {
+        final int height = drawable.getMinimumHeight();
+
+        canvas.save();
+        canvas.clipRect(bounds);
+
+        final int span = bounds.bottom - bounds.top;
+        if (span < height) {
+            bounds.top = bounds.bottom - height;
+        }
+
+        drawable.setBounds(bounds);
+        drawable.draw(canvas);
+
+        canvas.restore();
+    }
+
+    void drawOverscrollFooter(Canvas canvas, Drawable drawable, Rect bounds) {
+        final int height = drawable.getMinimumHeight();
+
+        canvas.save();
+        canvas.clipRect(bounds);
+
+        final int span = bounds.bottom - bounds.top;
+        if (span < height) {
+            bounds.bottom = bounds.top + height;
+        }
+
+        drawable.setBounds(bounds);
+        drawable.draw(canvas);
+
+        canvas.restore();
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         // Draw the dividers
         final int dividerHeight = mDividerHeight;
+        final Drawable overscrollHeader = mOverScrollHeader;
+        final Drawable overscrollFooter = mOverScrollFooter;
+        final boolean drawOverscrollHeader = overscrollHeader != null;
+        final boolean drawOverscrollFooter = overscrollFooter != null;
         final boolean drawDividers = dividerHeight > 0 && mDivider != null;
 
-        if (drawDividers) {
+        if (drawDividers || drawOverscrollHeader || drawOverscrollFooter) {
             // Only modify the top and bottom in the loop, we set the left and right here
             final Rect bounds = mTempRect;
             bounds.left = mPaddingLeft;
@@ -2998,34 +3051,67 @@
 
             final int listBottom = mBottom - mTop - mListPadding.bottom + mScrollY;
             if (!mStackFromBottom) {
-                int bottom;
+                int bottom = 0;
                 
+                // Draw top divider or header for overscroll
+                final int scrollY = mScrollY;
+                if (count > 0 && scrollY < 0) {
+                    if (drawOverscrollHeader) {
+                        bounds.bottom = 0;
+                        bounds.top = scrollY;
+                        drawOverscrollHeader(canvas, overscrollHeader, bounds);
+                    } else if (drawDividers) {
+                        bounds.bottom = 0;
+                        bounds.top = -dividerHeight;
+                        drawDivider(canvas, bounds, -1);
+                    }
+                }
+
                 for (int i = 0; i < count; i++) {
                     if ((headerDividers || first + i >= headerCount) &&
                             (footerDividers || first + i < footerLimit)) {
                         View child = getChildAt(i);
                         bottom = child.getBottom();
                         // Don't draw dividers next to items that are not enabled
-                        if ((areAllItemsSelectable ||
-                                (adapter.isEnabled(first + i) && (i == count - 1 ||
-                                        adapter.isEnabled(first + i + 1))))) {
-                            bounds.top = bottom;
-                            bounds.bottom = bottom + dividerHeight;
-                            drawDivider(canvas, bounds, i);
-                        } else if (fillForMissingDividers) {
-                            bounds.top = bottom;
-                            bounds.bottom = bottom + dividerHeight;
-                            canvas.drawRect(bounds, paint);
+
+                        if (drawDividers &&
+                                (bottom < listBottom && !(drawOverscrollFooter && i == count - 1))) {
+                            if ((areAllItemsSelectable ||
+                                    (adapter.isEnabled(first + i) && (i == count - 1 ||
+                                            adapter.isEnabled(first + i + 1))))) {
+                                bounds.top = bottom;
+                                bounds.bottom = bottom + dividerHeight;
+                                drawDivider(canvas, bounds, i);
+                            } else if (fillForMissingDividers) {
+                                bounds.top = bottom;
+                                bounds.bottom = bottom + dividerHeight;
+                                canvas.drawRect(bounds, paint);
+                            }
                         }
                     }
                 }
+
+                final int overFooterBottom = mBottom + mScrollY;
+                if (drawOverscrollFooter && first + count == itemCount &&
+                        overFooterBottom > bottom) {
+                    bounds.top = bottom;
+                    bounds.bottom = overFooterBottom;
+                    drawOverscrollFooter(canvas, overscrollFooter, bounds);
+                }
             } else {
                 int top;
                 int listTop = mListPadding.top;
 
                 final int scrollY = mScrollY;
 
-                for (int i = 0; i < count; i++) {
+                if (count > 0 && drawOverscrollHeader) {
+                    bounds.top = scrollY;
+                    bounds.bottom = getChildAt(0).getTop();
+                    drawOverscrollHeader(canvas, overscrollHeader, bounds);
+                }
+
+                final int start = drawOverscrollHeader ? 1 : 0;
+                for (int i = start; i < count; i++) {
                     if ((headerDividers || first + i >= headerCount) &&
                             (footerDividers || first + i < footerLimit)) {
                         View child = getChildAt(i);
@@ -3052,9 +3138,16 @@
                 }
                 
                 if (count > 0 && scrollY > 0) {
-                    bounds.top = listBottom;
-                    bounds.bottom = listBottom + dividerHeight;
-                    drawDivider(canvas, bounds, -1);
+                    if (drawOverscrollFooter) {
+                        final int absListBottom = mBottom;
+                        bounds.top = absListBottom;
+                        bounds.bottom = absListBottom + scrollY;
+                        drawOverscrollFooter(canvas, overscrollFooter, bounds);
+                    } else if (drawDividers) {
+                        bounds.top = listBottom;
+                        bounds.bottom = listBottom + dividerHeight;
+                        drawDivider(canvas, bounds, -1);
+                    }
                 }
             }
         }
@@ -3150,6 +3243,45 @@
         invalidate();
     }
     
+    /**
+     * Sets the drawable that will be drawn above all other list content.
+     * This area can become visible when the user overscrolls the list.
+     *
+     * @param header The drawable to use
+     */
+    public void setOverscrollHeader(Drawable header) {
+        mOverScrollHeader = header;
+        if (mScrollY < 0) {
+            invalidate();
+        }
+    }
+
+    /**
+     * @return The drawable that will be drawn above all other list content
+     */
+    public Drawable getOverscrollHeader() {
+        return mOverScrollHeader;
+    }
+
+    /**
+     * Sets the drawable that will be drawn below all other list content.
+     * This area can become visible when the user overscrolls the list,
+     * or when the list's content does not fully fill the container area.
+     *
+     * @param footer The drawable to use
+     */
+    public void setOverscrollFooter(Drawable footer) {
+        mOverScrollFooter = footer;
+        invalidate();
+    }
+
+    /**
+     * @return The drawable that will be drawn below all other list content
+     */
+    public Drawable getOverscrollFooter() {
+        return mOverScrollFooter;
+    }
+
     @Override
     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
new file mode 100644
index 0000000..cd81e31
--- /dev/null
+++ b/core/java/android/widget/OverScroller.java
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.graphics.Interpolator;
+import android.view.ViewConfiguration;
+import android.view.animation.AnimationUtils;
+
+/**
+ * This class encapsulates scrolling with the ability to overshoot the bounds
+ * of a scrolling operation. This class is a drop-in replacement for
+ * {@link android.widget.Scroller} in most cases.
+ */
+public class OverScroller {
+    int mMode;
+
+    private final MagneticOverScroller mScrollerX;
+    private final MagneticOverScroller mScrollerY;
+
+    private float mDeceleration;
+    private final float mPpi;
+    private final boolean mFlywheel;
+
+    private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));
+    private static float ALPHA = 800; // pixels / seconds
+    private static float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance)
+    private static float END_TENSION = 1.0f - START_TENSION;
+    private static final int NB_SAMPLES = 100;
+    private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1];
+    private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1];
+
+    private static final int DEFAULT_DURATION = 250;
+    private static final int SCROLL_MODE = 0;
+    private static final int FLING_MODE = 1;
+
+    static {
+        float x_min = 0.0f;
+        float y_min = 0.0f;
+        for (int i = 0; i < NB_SAMPLES; i++) {
+            final float alpha = (float) i / NB_SAMPLES;
+            {
+                float x_max = 1.0f;
+                float x, tx, coef;
+                while (true) {
+                    x = x_min + (x_max - x_min) / 2.0f;
+                    coef = 3.0f * x * (1.0f - x);
+                    tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x;
+                    if (Math.abs(tx - alpha) < 1E-5) break;
+                    if (tx > alpha) x_max = x;
+                    else x_min = x;
+                }
+                SPLINE_POSITION[i] = coef + x * x * x;
+            }
+
+            {
+                float y_max = 1.0f;
+                float y, dy, coef;
+                while (true) {
+                    y = y_min + (y_max - y_min) / 2.0f;
+                    coef = 3.0f * y * (1.0f - y);
+                    dy = coef + y * y * y;
+                    if (Math.abs(dy - alpha) < 1E-5) break;
+                    if (dy > alpha) y_max = y;
+                    else y_min = y;
+                }
+                SPLINE_TIME[i] = coef * ((1.0f - y) * START_TENSION + y * END_TENSION) + y * y * y;
+            }
+        }
+        SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f;
+    }
+
+    public OverScroller(Context context) {
+        this(context, null, 0.f, 0.f, true);
+    }
+
+    /**
+     * Creates an OverScroller.
+     * @param context The context of this application.
+     * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
+     * be used.
+     * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the
+     * velocity which is preserved in the bounce when the horizontal edge is reached. A null value
+     * means no bounce.
+     * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction.
+     */
+    public OverScroller(Context context, Interpolator interpolator,
+            float bounceCoefficientX, float bounceCoefficientY, boolean flywheel) {
+        mFlywheel = flywheel;
+        mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
+        mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
+        mScrollerX = new MagneticOverScroller();
+        mScrollerY = new MagneticOverScroller();
+
+        mScrollerX.setBounceCoefficient(bounceCoefficientX);
+        mScrollerY.setBounceCoefficient(bounceCoefficientY);
+    }
+
+
+    /**
+     * The amount of friction applied to flings. The default value
+     * is {@link ViewConfiguration#getScrollFriction}.
+     *
+     * @param friction A scalar dimension-less value representing the coefficient of
+     *         friction.
+     */
+    public final void setFriction(float friction) {
+        mDeceleration = computeDeceleration(friction);
+    }
+
+    private float computeDeceleration(float friction) {
+        return 9.81f   // g (m/s^2)
+        * 39.37f               // inch/meter
+        * mPpi                 // pixels per inch
+        * friction;
+    }
+
+    /**
+     *
+     * Returns whether the scroller has finished scrolling.
+     *
+     * @return True if the scroller has finished scrolling, false otherwise.
+     */
+    public final boolean isFinished() {
+        return mScrollerX.mFinished && mScrollerY.mFinished;
+    }
+
+    /**
+     * Force the finished field to a particular value. Contrary to
+     * {@link #abortAnimation()}, forcing the animation to finished
+     * does NOT cause the scroller to move to the final x and y
+     * position.
+     *
+     * @param finished The new finished value.
+     */
+    public final void forceFinished(boolean finished) {
+        mScrollerX.mFinished = mScrollerY.mFinished = finished;
+    }
+
+    /**
+     * Returns the current X offset in the scroll.
+     *
+     * @return The new X offset as an absolute distance from the origin.
+     */
+    public final int getCurrX() {
+        return mScrollerX.mCurrentPosition;
+    }
+
+    /**
+     * Returns the current Y offset in the scroll.
+     *
+     * @return The new Y offset as an absolute distance from the origin.
+     */
+    public final int getCurrY() {
+        return mScrollerY.mCurrentPosition;
+    }
+
+    /**
+     * @hide
+     * Returns the current velocity.
+     *
+     * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
+     */
+    public float getCurrVelocity() {
+        float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
+        squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
+        return (float) Math.sqrt(squaredNorm);
+    }
+
+    /**
+     * Returns the start X offset in the scroll.
+     *
+     * @return The start X offset as an absolute distance from the origin.
+     */
+    public final int getStartX() {
+        return mScrollerX.mStart;
+    }
+
+    /**
+     * Returns the start Y offset in the scroll.
+     *
+     * @return The start Y offset as an absolute distance from the origin.
+     */
+    public final int getStartY() {
+        return mScrollerY.mStart;
+    }
+
+    /**
+     * Returns where the scroll will end. Valid only for "fling" scrolls.
+     *
+     * @return The final X offset as an absolute distance from the origin.
+     */
+    public final int getFinalX() {
+        return mScrollerX.mFinal;
+    }
+
+    /**
+     * Returns where the scroll will end. Valid only for "fling" scrolls.
+     *
+     * @return The final Y offset as an absolute distance from the origin.
+     */
+    public final int getFinalY() {
+        return mScrollerY.mFinal;
+    }
+
+    /**
+     * Returns how long the scroll event will take, in milliseconds.
+     *
+     * @return The duration of the scroll in milliseconds.
+     *
+     * @hide Pending removal once nothing depends on it
+     * @deprecated OverScrollers don't necessarily have a fixed duration.
+     *             This function will lie to the best of its ability.
+     */
+    @Deprecated
+    public final int getDuration() {
+        return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
+    }
+
+    /**
+     * Extend the scroll animation. This allows a running animation to scroll
+     * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
+     *
+     * @param extend Additional time to scroll in milliseconds.
+     * @see #setFinalX(int)
+     * @see #setFinalY(int)
+     *
+     * @hide Pending removal once nothing depends on it
+     * @deprecated OverScrollers don't necessarily have a fixed duration.
+     *             Instead of setting a new final position and extending
+     *             the duration of an existing scroll, use startScroll
+     *             to begin a new animation.
+     */
+    @Deprecated
+    public void extendDuration(int extend) {
+        mScrollerX.extendDuration(extend);
+        mScrollerY.extendDuration(extend);
+    }
+
+    /**
+     * Sets the final position (X) for this scroller.
+     *
+     * @param newX The new X offset as an absolute distance from the origin.
+     * @see #extendDuration(int)
+     * @see #setFinalY(int)
+     *
+     * @hide Pending removal once nothing depends on it
+     * @deprecated OverScroller's final position may change during an animation.
+     *             Instead of setting a new final position and extending
+     *             the duration of an existing scroll, use startScroll
+     *             to begin a new animation.
+     */
+    @Deprecated
+    public void setFinalX(int newX) {
+        mScrollerX.setFinalPosition(newX);
+    }
+
+    /**
+     * Sets the final position (Y) for this scroller.
+     *
+     * @param newY The new Y offset as an absolute distance from the origin.
+     * @see #extendDuration(int)
+     * @see #setFinalX(int)
+     *
+     * @hide Pending removal once nothing depends on it
+     * @deprecated OverScroller's final position may change during an animation.
+     *             Instead of setting a new final position and extending
+     *             the duration of an existing scroll, use startScroll
+     *             to begin a new animation.
+     */
+    @Deprecated
+    public void setFinalY(int newY) {
+        mScrollerY.setFinalPosition(newY);
+    }
+
+    /**
+     * Call this when you want to know the new location. If it returns true, the
+     * animation is not yet finished.
+     */
+    public boolean computeScrollOffset() {
+        if (isFinished()) {
+            return false;
+        }
+
+        switch (mMode) {
+            case SCROLL_MODE:
+                long time = AnimationUtils.currentAnimationTimeMillis();
+                // Any scroller can be used for time, since they were started
+                // together in scroll mode. We use X here.
+                final long elapsedTime = time - mScrollerX.mStartTime;
+
+                final int duration = mScrollerX.mDuration;
+                if (elapsedTime < duration) {
+                    float q = (float) (elapsedTime) / duration;
+
+                    q = Scroller.viscousFluid(q);
+
+                    mScrollerX.updateScroll(q);
+                    mScrollerY.updateScroll(q);
+                } else {
+                    abortAnimation();
+                }
+                break;
+
+            case FLING_MODE:
+                if (!mScrollerX.mFinished) {
+                    if (!mScrollerX.update()) {
+                        if (!mScrollerX.continueWhenFinished()) {
+                            mScrollerX.finish();
+                        }
+                    }
+                }
+
+                if (!mScrollerY.mFinished) {
+                    if (!mScrollerY.update()) {
+                        if (!mScrollerY.continueWhenFinished()) {
+                            mScrollerY.finish();
+                        }
+                    }
+                }
+
+                break;
+        }
+
+        return true;
+    }
+
+    /**
+     * Start scrolling by providing a starting point and the distance to travel.
+     * The scroll will use the default value of 250 milliseconds for the
+     * duration.
+     *
+     * @param startX Starting horizontal scroll offset in pixels. Positive
+     *        numbers will scroll the content to the left.
+     * @param startY Starting vertical scroll offset in pixels. Positive numbers
+     *        will scroll the content up.
+     * @param dx Horizontal distance to travel. Positive numbers will scroll the
+     *        content to the left.
+     * @param dy Vertical distance to travel. Positive numbers will scroll the
+     *        content up.
+     */
+    public void startScroll(int startX, int startY, int dx, int dy) {
+        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
+    }
+
+    /**
+     * Start scrolling by providing a starting point and the distance to travel.
+     *
+     * @param startX Starting horizontal scroll offset in pixels. Positive
+     *        numbers will scroll the content to the left.
+     * @param startY Starting vertical scroll offset in pixels. Positive numbers
+     *        will scroll the content up.
+     * @param dx Horizontal distance to travel. Positive numbers will scroll the
+     *        content to the left.
+     * @param dy Vertical distance to travel. Positive numbers will scroll the
+     *        content up.
+     * @param duration Duration of the scroll in milliseconds.
+     */
+    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
+        mMode = SCROLL_MODE;
+        mScrollerX.startScroll(startX, dx, duration);
+        mScrollerY.startScroll(startY, dy, duration);
+    }
+
+    /**
+     * Call this when you want to 'spring back' into a valid coordinate range.
+     *
+     * @param startX Starting X coordinate
+     * @param startY Starting Y coordinate
+     * @param minX Minimum valid X value
+     * @param maxX Maximum valid X value
+     * @param minY Minimum valid Y value
+     * @param maxY Minimum valid Y value
+     * @return true if a springback was initiated, false if startX and startY were
+     *          already within the valid range.
+     */
+    public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
+        mMode = FLING_MODE;
+
+        // Make sure both methods are called.
+        final boolean spingbackX = mScrollerX.springback(startX, minX, maxX);
+        final boolean spingbackY = mScrollerY.springback(startY, minY, maxY);
+        return spingbackX || spingbackY;
+    }
+
+    public void fling(int startX, int startY, int velocityX, int velocityY,
+            int minX, int maxX, int minY, int maxY) {
+        fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
+    }
+
+    /**
+     * Start scrolling based on a fling gesture. The distance traveled will
+     * depend on the initial velocity of the fling.
+     *
+     * @param startX Starting point of the scroll (X)
+     * @param startY Starting point of the scroll (Y)
+     * @param velocityX Initial velocity of the fling (X) measured in pixels per
+     *            second.
+     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
+     *            second
+     * @param minX Minimum X value. The scroller will not scroll past this point
+     *            unless overX > 0. If overfling is allowed, it will use minX as
+     *            a springback boundary.
+     * @param maxX Maximum X value. The scroller will not scroll past this point
+     *            unless overX > 0. If overfling is allowed, it will use maxX as
+     *            a springback boundary.
+     * @param minY Minimum Y value. The scroller will not scroll past this point
+     *            unless overY > 0. If overfling is allowed, it will use minY as
+     *            a springback boundary.
+     * @param maxY Maximum Y value. The scroller will not scroll past this point
+     *            unless overY > 0. If overfling is allowed, it will use maxY as
+     *            a springback boundary.
+     * @param overX Overfling range. If > 0, horizontal overfling in either
+     *            direction will be possible.
+     * @param overY Overfling range. If > 0, vertical overfling in either
+     *            direction will be possible.
+     */
+    public void fling(int startX, int startY, int velocityX, int velocityY,
+            int minX, int maxX, int minY, int maxY, int overX, int overY) {
+        // Continue a scroll or fling in progress
+        if (mFlywheel && !isFinished()) {
+            float oldVelocityX = mScrollerX.mCurrVelocity;
+            float oldVelocityY = mScrollerY.mCurrVelocity;
+            if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
+                    Math.signum(velocityY) == Math.signum(oldVelocityY)) {
+                velocityX += oldVelocityX;
+                velocityY += oldVelocityY;
+            }
+        }
+
+        mMode = FLING_MODE;
+        mScrollerX.fling(startX, velocityX, minX, maxX, overX);
+        mScrollerY.fling(startY, velocityY, minY, maxY, overY);
+    }
+
+    /**
+     * Notify the scroller that we've reached a horizontal boundary.
+     * Normally the information to handle this will already be known
+     * when the animation is started, such as in a call to one of the
+     * fling functions. However there are cases where this cannot be known
+     * in advance. This function will transition the current motion and
+     * animate from startX to finalX as appropriate.
+     *
+     * @param startX Starting/current X position
+     * @param finalX Desired final X position
+     * @param overX Magnitude of overscroll allowed. This should be the maximum
+     *              desired distance from finalX. Absolute value - must be positive.
+     */
+    public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
+        mScrollerX.notifyEdgeReached(startX, finalX, overX);
+    }
+
+    /**
+     * Notify the scroller that we've reached a vertical boundary.
+     * Normally the information to handle this will already be known
+     * when the animation is started, such as in a call to one of the
+     * fling functions. However there are cases where this cannot be known
+     * in advance. This function will animate a parabolic motion from
+     * startY to finalY.
+     *
+     * @param startY Starting/current Y position
+     * @param finalY Desired final Y position
+     * @param overY Magnitude of overscroll allowed. This should be the maximum
+     *              desired distance from finalY. Absolute value - must be positive.
+     */
+    public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
+        mScrollerY.notifyEdgeReached(startY, finalY, overY);
+    }
+
+    /**
+     * Returns whether the current Scroller is currently returning to a valid position.
+     * Valid bounds were provided by the
+     * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method.
+     *
+     * One should check this value before calling
+     * {@link #startScroll(int, int, int, int)} as the interpolation currently in progress
+     * to restore a valid position will then be stopped. The caller has to take into account
+     * the fact that the started scroll will start from an overscrolled position.
+     *
+     * @return true when the current position is overscrolled and in the process of
+     *         interpolating back to a valid value.
+     */
+    public boolean isOverScrolled() {
+        return ((!mScrollerX.mFinished &&
+                mScrollerX.mState != MagneticOverScroller.TO_EDGE) ||
+                (!mScrollerY.mFinished &&
+                        mScrollerY.mState != MagneticOverScroller.TO_EDGE));
+    }
+
+    /**
+     * Stops the animation. Contrary to {@link #forceFinished(boolean)},
+     * aborting the animating causes the scroller to move to the final x and y
+     * positions.
+     *
+     * @see #forceFinished(boolean)
+     */
+    public void abortAnimation() {
+        mScrollerX.finish();
+        mScrollerY.finish();
+    }
+
+    /**
+     * Returns the time elapsed since the beginning of the scrolling.
+     *
+     * @return The elapsed time in milliseconds.
+     *
+     * @hide
+     */
+    public int timePassed() {
+        final long time = AnimationUtils.currentAnimationTimeMillis();
+        final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
+        return (int) (time - startTime);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isScrollingInDirection(float xvel, float yvel) {
+        final int dx = mScrollerX.mFinal - mScrollerX.mStart;
+        final int dy = mScrollerY.mFinal - mScrollerY.mStart;
+        return !isFinished() && Math.signum(xvel) == Math.signum(dx) &&
+        Math.signum(yvel) == Math.signum(dy);
+    }
+
+    class MagneticOverScroller {
+        // Initial position
+        int mStart;
+
+        // Current position
+        int mCurrentPosition;
+
+        // Final position
+        int mFinal;
+
+        // Initial velocity
+        int mVelocity;
+
+        // Current velocity
+        float mCurrVelocity;
+
+        // Constant current deceleration
+        float mDeceleration;
+
+        // Animation starting time, in system milliseconds
+        long mStartTime;
+
+        // Animation duration, in milliseconds
+        int mDuration;
+
+        // Duration to complete spline component of animation
+        int mSplineDuration;
+
+        // Distance to travel along spline animation
+        int mSplineDistance;
+
+        // Whether the animation is currently in progress
+        boolean mFinished;
+
+        private static final int TO_EDGE = 0;
+        private static final int TO_BOUNDARY = 1;
+        private static final int TO_BOUNCE = 2;
+
+        private int mState = TO_EDGE;
+
+        // The allowed overshot distance before boundary is reached.
+        private int mOver;
+
+        // If the velocity is smaller than this value, no bounce is triggered
+        // when the edge limits are reached (would result in a zero pixels
+        // displacement anyway).
+        private static final float MINIMUM_VELOCITY_FOR_BOUNCE = 140.0f; //Float.MAX_VALUE;//140.0f;
+
+        // Proportion of the velocity that is preserved when the edge is reached.
+        private static final float DEFAULT_BOUNCE_COEFFICIENT = 0.36f;
+
+        private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT;
+
+        MagneticOverScroller() {
+            mFinished = true;
+        }
+
+        void updateScroll(float q) {
+            mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
+        }
+
+        /*
+         * Get a signed deceleration that will reduce the velocity.
+         */
+        float getDeceleration(int velocity) {
+            return velocity > 0 ? -OverScroller.this.mDeceleration : OverScroller.this.mDeceleration;
+        }
+
+        /*
+         * Modifies mDuration to the duration it takes to get from start to newFinal using the
+         * spline interpolation. The previous duration was needed to get to oldFinal.
+         */
+        void adjustDuration(int start, int oldFinal, int newFinal) {
+            final int oldDistance = oldFinal - start;
+            final int newDistance = newFinal - start;
+            final float x = (float) Math.abs((float) newDistance / oldDistance);
+            final int index = (int) (NB_SAMPLES * x);
+            if (index < NB_SAMPLES) {
+                final float x_inf = (float) index / NB_SAMPLES;
+                final float x_sup = (float) (index + 1) / NB_SAMPLES;
+                final float t_inf = SPLINE_TIME[index];
+                final float t_sup = SPLINE_TIME[index + 1];
+                final float timeCoef = t_inf + (x - x_inf) / (x_sup - x_inf) * (t_sup - t_inf);
+
+                mDuration *= timeCoef;
+            }
+        }
+
+        void startScroll(int start, int distance, int duration) {
+            mFinished = false;
+
+            mStart = start;
+            mFinal = start + distance;
+
+            mStartTime = AnimationUtils.currentAnimationTimeMillis();
+            mDuration = duration;
+
+            // Unused
+            mDeceleration = 0.0f;
+            mVelocity = 0;
+        }
+
+        void finish() {
+            mCurrentPosition = mFinal;
+            // Not reset since WebView relies on this value for fast fling.
+            // TODO: restore when WebView uses the fast fling implemented in this class.
+            // mCurrVelocity = 0.0f;
+            mFinished = true;
+        }
+
+        void setFinalPosition(int position) {
+            mFinal = position;
+            mFinished = false;
+        }
+
+        void extendDuration(int extend) {
+            final long time = AnimationUtils.currentAnimationTimeMillis();
+            final int elapsedTime = (int) (time - mStartTime);
+            mDuration = elapsedTime + extend;
+            mFinished = false;
+        }
+
+        void setBounceCoefficient(float coefficient) {
+            mBounceCoefficient = coefficient;
+        }
+
+        boolean springback(int start, int min, int max) {
+            mFinished = true;
+
+            mStart = mFinal = start;
+            mVelocity = 0;
+
+            mStartTime = AnimationUtils.currentAnimationTimeMillis();
+            mDuration = 0;
+
+            if (start < min) {
+                startSpringback(start, min, 0);
+            } else if (start > max) {
+                startSpringback(start, max, 0);
+            }
+
+            return !mFinished;
+        }
+
+        private void startSpringback(int start, int end, int velocity) {
+            mFinished = false;
+            mState = TO_BOUNCE;
+            mStart = mFinal = end;
+            final float velocitySign = Math.signum(start - end);
+            mDeceleration = getDeceleration((int) velocitySign);
+            fitOnBounceCurve(start, end, velocity);
+            mDuration = - (int) (2000.0f * mVelocity / mDeceleration);
+        }
+
+        void fling(int start, int velocity, int min, int max, int over) {
+            mOver = over;
+            mFinished = false;
+            mCurrVelocity = mVelocity = velocity;
+            mDuration = mSplineDuration = 0;
+            mStartTime = AnimationUtils.currentAnimationTimeMillis();
+            mStart = start;
+
+            if (start > max || start < min) {
+                startAfterEdge(start, min, max, velocity);
+                return;
+            }
+
+            mState = TO_EDGE;
+            double totalDistance = 0.0;
+
+            if (velocity != 0) {
+                final double l = Math.log(START_TENSION * Math.abs(velocity) / ALPHA);
+                // Duration are expressed in milliseconds
+                mDuration = mSplineDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0)));
+                totalDistance = (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
+            }
+
+            mSplineDistance = (int) (totalDistance * Math.signum(velocity));
+            mFinal = start + mSplineDistance;
+
+            // Clamp to a valid final position
+            if (mFinal < min) {
+                adjustDuration(mStart, mFinal, min);
+                mFinal = min;
+            }
+
+            if (mFinal > max) {
+                adjustDuration(mStart, mFinal, max);
+                mFinal = max;
+            }
+        }
+
+        private void fitOnBounceCurve(int start, int end, int velocity) {
+            // Simulate a bounce that started from edge
+            final float durationToApex = - velocity / mDeceleration;
+            final float distanceToApex = velocity * velocity / 2.0f / Math.abs(mDeceleration);
+            final float distanceToEdge = Math.abs(end - start);
+            final float totalDuration = (float) Math.sqrt(
+                    2.0 * (distanceToApex + distanceToEdge) / Math.abs(mDeceleration));
+            mStartTime -= (int) (1000.0f * (totalDuration - durationToApex));
+            mStart = end;
+            mVelocity = (int) (- mDeceleration * totalDuration);
+        }
+
+        private void startBounceAfterEdge(int start, int end, int velocity) {
+            mDeceleration = getDeceleration(velocity == 0 ? start - end : velocity);
+            fitOnBounceCurve(start, end, velocity);
+            onEdgeReached();
+        }
+
+        private void startAfterEdge(int start, int min, int max, int velocity) {
+            if (start > min && start < max) {
+                mFinished = true;
+                return;
+            }
+            final boolean positive = start > max;
+            final int edge = positive ? max : min;
+            final int overDistance = start - edge;
+            boolean keepIncreasing = overDistance * velocity >= 0;
+            if (keepIncreasing) {
+                // Will result in a bounce or a to_boundary depending on velocity.
+                startBounceAfterEdge(start, edge, velocity);
+            } else {
+                final double l = Math.log(START_TENSION * Math.abs(velocity) / ALPHA);
+                final double totalDistance =
+                    (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
+                if (totalDistance > Math.abs(overDistance)) {
+                    fling(start, velocity, positive ? min : start, positive ? start : max, mOver);
+                } else {
+                    startSpringback(start, edge, velocity);
+                }
+            }
+        }
+
+        void notifyEdgeReached(int start, int end, int over) {
+            mOver = over;
+            mStartTime = AnimationUtils.currentAnimationTimeMillis();
+            // We were in fling/scroll mode before: current velocity is such that distance to edge
+            // is increasing. Ensures that startAfterEdge will not start a new fling.
+            startAfterEdge(start, end, end, (int) mCurrVelocity);
+        }
+
+        private void onEdgeReached() {
+            // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached.
+            final float distance = - mVelocity * mVelocity / (2.0f * mDeceleration);
+
+            if (Math.abs(distance) < mOver) {
+                // Spring force will bring us back to final position
+                mState = TO_BOUNCE;
+                mFinal = mStart;
+                mDuration = - (int) (2000.0f * mVelocity / mDeceleration);
+            } else {
+                // Velocity is too high, we will hit the boundary limit
+                mState = TO_BOUNDARY;
+                int over = mVelocity > 0 ? mOver : -mOver;
+                mFinal = mStart + over;
+                mDuration = (int) (1000.0 * Math.PI * over / 2.0 / mVelocity);
+            }
+        }
+
+        boolean continueWhenFinished() {
+            switch (mState) {
+                case TO_EDGE:
+                    // Duration from start to null velocity
+                    if (mDuration < mSplineDuration) {
+                        // If the animation was clamped, we reached the edge
+                        mStart = mFinal;
+                        // Speed when edge was reached
+                        mVelocity = (int) mCurrVelocity;
+                        mDeceleration = getDeceleration(mVelocity);
+                        mStartTime += mDuration;
+                        onEdgeReached();
+                    } else {
+                        // Normal stop, no need to continue
+                        return false;
+                    }
+                    break;
+                case TO_BOUNDARY:
+                    mStartTime += mDuration;
+                    startSpringback(mFinal, mFinal - (mVelocity > 0 ? mOver:-mOver), 0);
+                    break;
+                case TO_BOUNCE:
+                    mVelocity = (int) (mVelocity * mBounceCoefficient);
+                    if (Math.abs(mVelocity) < MINIMUM_VELOCITY_FOR_BOUNCE) {
+                        return false;
+                    }
+                    mStartTime += mDuration;
+                    mDuration = - (int) (mVelocity / mDeceleration);
+                    break;
+            }
+
+            update();
+            return true;
+        }
+
+        /*
+         * Update the current position and velocity for current time. Returns
+         * true if update has been done and false if animation duration has been
+         * reached.
+         */
+        boolean update() {
+            final long time = AnimationUtils.currentAnimationTimeMillis();
+            final long currentTime = time - mStartTime;
+
+            if (currentTime > mDuration) {
+                return false;
+            }
+
+            double distance = 0.0;
+            switch (mState) {
+                case TO_EDGE: {
+                    final float t = (float) currentTime / mSplineDuration;
+                    final int index = (int) (NB_SAMPLES * t);
+                    float distanceCoef = 1.f;
+                    float velocityCoef = 0.f;
+                    if (index < NB_SAMPLES) {
+                        final float t_inf = (float) index / NB_SAMPLES;
+                        final float t_sup = (float) (index + 1) / NB_SAMPLES;
+                        final float d_inf = SPLINE_POSITION[index];
+                        final float d_sup = SPLINE_POSITION[index + 1];
+                        velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
+                        distanceCoef = d_inf + (t - t_inf) * velocityCoef;
+                    }
+
+                    distance = distanceCoef * mSplineDistance;
+                    mCurrVelocity = velocityCoef * mSplineDistance / mSplineDuration * 1000;
+                    break;
+                }
+
+                case TO_BOUNCE: {
+                    final float t = currentTime / 1000.0f;
+                    mCurrVelocity = mVelocity + mDeceleration * t;
+                    distance = mVelocity * t + mDeceleration * t * t / 2.0f;
+                    break;
+                }
+
+                case TO_BOUNDARY: {
+                    final float t = currentTime / 1000.0f;
+                    final float d = t * Math.abs(mVelocity) / mOver;
+                    mCurrVelocity = mVelocity * (float) Math.cos(d);
+                    distance = (mVelocity > 0 ? mOver : -mOver) * Math.sin(d);
+                    break;
+                }
+            }
+
+            mCurrentPosition = mStart + (int) Math.round(distance);
+            return true;
+        }
+    }
+}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index eb527eb..900c9ec 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -19,8 +19,12 @@
 import com.android.internal.R;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.StrictMode;
 import android.util.AttributeSet;
 import android.view.FocusFinder;
 import android.view.KeyEvent;
@@ -61,7 +65,9 @@
     private long mLastScroll;
 
     private final Rect mTempRect = new Rect();
-    private Scroller mScroller;
+    private OverScroller mScroller;
+    private EdgeGlow mEdgeGlowTop;
+    private EdgeGlow mEdgeGlowBottom;
 
     /**
      * Flag to indicate that we are moving focus ourselves. This is so the
@@ -115,12 +121,24 @@
     private int mMinimumVelocity;
     private int mMaximumVelocity;
     
+    private int mOverscrollDistance;
+    private int mOverflingDistance;
+
     /**
      * ID of the active pointer. This is used to retain consistency during
      * drags/flings if multiple pointers are used.
      */
     private int mActivePointerId = INVALID_POINTER;
-    
+
+    /**
+     * The StrictMode "critical time span" objects to catch animation
+     * stutters.  Non-null when a time-sensitive animation is
+     * in-flight.  Must call finish() on them when done animating.
+     * These are no-ops on user builds.
+     */
+    private StrictMode.Span mScrollStrictSpan = null;  // aka "drag"
+    private StrictMode.Span mFlingStrictSpan = null;
+
     /**
      * Sentinel value for no current active pointer.
      * Used by {@link #mActivePointerId}.
@@ -187,7 +205,7 @@
 
 
     private void initScrollView() {
-        mScroller = new Scroller(getContext());
+        mScroller = new OverScroller(getContext());
         setFocusable(true);
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
         setWillNotDraw(false);
@@ -195,6 +213,8 @@
         mTouchSlop = configuration.getScaledTouchSlop();
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+        mOverscrollDistance = configuration.getScaledOverscrollDistance();
+        mOverflingDistance = configuration.getScaledOverflingDistance();
     }
 
     @Override
@@ -427,6 +447,9 @@
                 if (yDiff > mTouchSlop) {
                     mIsBeingDragged = true;
                     mLastMotionY = y;
+                    if (mScrollStrictSpan == null) {
+                        mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
+                    }
                 }
                 break;
             }
@@ -451,6 +474,9 @@
                 * being flinged.
                 */
                 mIsBeingDragged = !mScroller.isFinished();
+                if (mIsBeingDragged && mScrollStrictSpan == null) {
+                    mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
+                }
                 break;
             }
 
@@ -459,6 +485,9 @@
                 /* Release the drag */
                 mIsBeingDragged = false;
                 mActivePointerId = INVALID_POINTER;
+                if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
+                    invalidate();
+                }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
@@ -491,9 +520,7 @@
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 final float y = ev.getY();
-                if (!(mIsBeingDragged = inChild((int) ev.getX(), (int) y))) {
-                    return false;
-                }
+                mIsBeingDragged = true;
                 
                 /*
                  * If being flinged and user touches, stop the fling. isFinished
@@ -501,6 +528,10 @@
                  */
                 if (!mScroller.isFinished()) {
                     mScroller.abortAnimation();
+                    if (mFlingStrictSpan != null) {
+                        mFlingStrictSpan.finish();
+                        mFlingStrictSpan = null;
+                    }
                 }
 
                 // Remember where the motion event started
@@ -516,7 +547,36 @@
                     final int deltaY = (int) (mLastMotionY - y);
                     mLastMotionY = y;
 
-                    scrollBy(0, deltaY);
+                    final int oldX = mScrollX;
+                    final int oldY = mScrollY;
+                    final int range = getScrollRange();
+                    if (overScrollBy(0, deltaY, 0, mScrollY, 0, range,
+                            0, mOverscrollDistance, true)) {
+                        // Break our velocity if we hit a scroll barrier.
+                        mVelocityTracker.clear();
+                    }
+                    onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+
+                    final int overscrollMode = getOverScrollMode();
+                    if (overscrollMode == OVER_SCROLL_ALWAYS ||
+                            (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
+                        final int pulledToY = oldY + deltaY;
+                        if (pulledToY < 0) {
+                            mEdgeGlowTop.onPull((float) deltaY / getHeight());
+                            if (!mEdgeGlowBottom.isFinished()) {
+                                mEdgeGlowBottom.onRelease();
+                            }
+                        } else if (pulledToY > range) {
+                            mEdgeGlowBottom.onPull((float) deltaY / getHeight());
+                            if (!mEdgeGlowTop.isFinished()) {
+                                mEdgeGlowTop.onRelease();
+                            }
+                        }
+                        if (mEdgeGlowTop != null
+                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
+                            invalidate();
+                        }
+                    }
                 }
                 break;
             case MotionEvent.ACTION_UP: 
@@ -525,27 +585,28 @@
                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                     int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
 
-                    if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) {
-                        fling(-initialVelocity);
+                    if (getChildCount() > 0) {
+                        if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+                            fling(-initialVelocity);
+                        } else {
+                            final int bottom = getScrollRange();
+                            if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, bottom)) {
+                                invalidate();
+                            }
+                        }
                     }
 
                     mActivePointerId = INVALID_POINTER;
-                    mIsBeingDragged = false;
-
-                    if (mVelocityTracker != null) {
-                        mVelocityTracker.recycle();
-                        mVelocityTracker = null;
-                    }
+                    endDrag();
                 }
                 break;
             case MotionEvent.ACTION_CANCEL:
                 if (mIsBeingDragged && getChildCount() > 0) {
-                    mActivePointerId = INVALID_POINTER;
-                    mIsBeingDragged = false;
-                    if (mVelocityTracker != null) {
-                        mVelocityTracker.recycle();
-                        mVelocityTracker = null;
+                    if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
+                        invalidate();
                     }
+                    mActivePointerId = INVALID_POINTER;
+                    endDrag();
                 }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
@@ -572,6 +633,32 @@
         }
     }
     
+    @Override
+    protected void onOverScrolled(int scrollX, int scrollY,
+            boolean clampedX, boolean clampedY) {
+        // Treat animating scrolls differently; see #computeScroll() for why.
+        if (!mScroller.isFinished()) {
+            mScrollX = scrollX;
+            mScrollY = scrollY;
+            if (clampedY) {
+                mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange());
+            }
+        } else {
+            super.scrollTo(scrollX, scrollY);
+        }
+        awakenScrollBars();
+    }
+
+    private int getScrollRange() {
+        int scrollRange = 0;
+        if (getChildCount() > 0) {
+            View child = getChildAt(0);
+            scrollRange = Math.max(0,
+                    child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop));
+        }
+        return scrollRange;
+    }
+
     /**
      * <p>
      * Finds the next focusable component that fits in this View's bounds
@@ -920,6 +1007,10 @@
         } else {
             if (!mScroller.isFinished()) {
                 mScroller.abortAnimation();
+                if (mFlingStrictSpan != null) {
+                    mFlingStrictSpan.finish();
+                    mFlingStrictSpan = null;
+                }
             }
             scrollBy(dx, dy);
         }
@@ -948,7 +1039,16 @@
             return contentHeight;
         }
         
-        return getChildAt(0).getBottom();
+        int scrollRange = getChildAt(0).getBottom();
+        final int scrollY = mScrollY;
+        final int overscrollBottom = Math.max(0, scrollRange - contentHeight);
+        if (scrollY < 0) {
+            scrollRange -= scrollY;
+        } else if (scrollY > overscrollBottom) {
+            scrollRange += scrollY - overscrollBottom;
+        }
+
+        return scrollRange;
     }
 
     @Override
@@ -1009,20 +1109,31 @@
             int x = mScroller.getCurrX();
             int y = mScroller.getCurrY();
 
-            if (getChildCount() > 0) {
-                View child = getChildAt(0);
-                x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
-                y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
-                if (x != oldX || y != oldY) {
-                    mScrollX = x;
-                    mScrollY = y;
-                    onScrollChanged(x, y, oldX, oldY);
+            if (oldX != x || oldY != y) {
+                overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, getScrollRange(),
+                        0, mOverflingDistance, false);
+                onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+
+                final int range = getScrollRange();
+                final int overscrollMode = getOverScrollMode();
+                if (overscrollMode == OVER_SCROLL_ALWAYS ||
+                        (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
+                    if (y < 0 && oldY >= 0) {
+                        mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
+                    } else if (y > range && oldY <= range) {
+                        mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
+                    }
                 }
             }
             awakenScrollBars();
 
             // Keep on drawing until the animation has finished.
             postInvalidate();
+        } else {
+            if (mFlingStrictSpan != null) {
+                mFlingStrictSpan.finish();
+                mFlingStrictSpan = null;
+            }
         }
     }
 
@@ -1197,6 +1308,20 @@
     }
 
     @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        if (mScrollStrictSpan != null) {
+            mScrollStrictSpan.finish();
+            mScrollStrictSpan = null;
+        }
+        if (mFlingStrictSpan != null) {
+            mFlingStrictSpan.finish();
+            mFlingStrictSpan = null;
+        }
+    }
+
+    @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
         mIsLayoutDirty = false;
@@ -1254,7 +1379,7 @@
             int bottom = getChildAt(0).getHeight();
     
             mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, 
-                    Math.max(0, bottom - height));
+                    Math.max(0, bottom - height), 0, height/2);
     
             final boolean movingDown = velocityY > 0;
     
@@ -1269,11 +1394,34 @@
                 mScrollViewMovedFocus = true;
                 mScrollViewMovedFocus = false;
             }
-    
+
+            if (mFlingStrictSpan == null) {
+                mFlingStrictSpan = StrictMode.enterCriticalSpan("ScrollView-fling");
+            }
+
             invalidate();
         }
     }
 
+    private void endDrag() {
+        mIsBeingDragged = false;
+
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+
+        if (mEdgeGlowTop != null) {
+            mEdgeGlowTop.onRelease();
+            mEdgeGlowBottom.onRelease();
+        }
+
+        if (mScrollStrictSpan != null) {
+            mScrollStrictSpan.finish();
+            mScrollStrictSpan = null;
+        }
+    }
+
     /**
      * {@inheritDoc}
      *
@@ -1292,6 +1440,55 @@
         }
     }
 
+    @Override
+    public void setOverScrollMode(int mode) {
+        if (mode != OVER_SCROLL_NEVER) {
+            if (mEdgeGlowTop == null) {
+                final Resources res = getContext().getResources();
+                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
+                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
+                mEdgeGlowTop = new EdgeGlow(edge, glow);
+                mEdgeGlowBottom = new EdgeGlow(edge, glow);
+            }
+        } else {
+            mEdgeGlowTop = null;
+            mEdgeGlowBottom = null;
+        }
+        super.setOverScrollMode(mode);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        if (mEdgeGlowTop != null) {
+            final int scrollY = mScrollY;
+            if (!mEdgeGlowTop.isFinished()) {
+                final int restoreCount = canvas.save();
+                final int width = getWidth();
+
+                canvas.translate(-width / 2, Math.min(0, scrollY));
+                mEdgeGlowTop.setSize(width * 2, getHeight());
+                if (mEdgeGlowTop.draw(canvas)) {
+                    invalidate();
+                }
+                canvas.restoreToCount(restoreCount);
+            }
+            if (!mEdgeGlowBottom.isFinished()) {
+                final int restoreCount = canvas.save();
+                final int width = getWidth();
+                final int height = getHeight();
+
+                canvas.translate(-width / 2, Math.max(getScrollRange(), scrollY) + height);
+                canvas.rotate(180, width, 0);
+                mEdgeGlowBottom.setSize(width * 2, height);
+                if (mEdgeGlowBottom.draw(canvas)) {
+                    invalidate();
+                }
+                canvas.restoreToCount(restoreCount);
+            }
+        }
+    }
+
     private int clamp(int n, int my, int child) {
         if (my >= child || n < 0) {
             /* my >= child is this case:
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index b1f50ba..f00640e 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -52,14 +52,10 @@
     private float mDurationReciprocal;
     private float mDeltaX;
     private float mDeltaY;
-    private float mViscousFluidScale;
-    private float mViscousFluidNormalize;
     private boolean mFinished;
     private Interpolator mInterpolator;
     private boolean mFlywheel;
 
-    private float mCoeffX = 0.0f;
-    private float mCoeffY = 1.0f;
     private float mVelocity;
 
     private static final int DEFAULT_DURATION = 250;
@@ -94,8 +90,17 @@
             SPLINE[i] = d;
         }
         SPLINE[NB_SAMPLES] = 1.0f;
+
+        // This controls the viscous fluid effect (how much of it)
+        sViscousFluidScale = 8.0f;
+        // must be set to 1.0 (used in viscousFluid())
+        sViscousFluidNormalize = 1.0f;
+        sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
     }
 
+    private static float sViscousFluidScale;
+    private static float sViscousFluidNormalize;
+
     /**
      * Create a Scroller with the default duration and interpolator.
      */
@@ -103,6 +108,11 @@
         this(context, null);
     }
 
+    /**
+     * Create a Scroller with the specified interpolator. If the interpolator is
+     * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
+     * be in effect for apps targeting Honeycomb or newer.
+     */
     public Scroller(Context context, Interpolator interpolator) {
         this(context, interpolator,
                 context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
@@ -110,7 +120,8 @@
 
     /**
      * Create a Scroller with the specified interpolator. If the interpolator is
-     * null, the default (viscous) interpolator will be used.
+     * null, the default (viscous) interpolator will be used. Specify whether or
+     * not to support progressive "flywheel" behavior in flinging.
      */
     public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
         mFinished = true;
@@ -332,12 +343,7 @@
         mFinalY = startY + dy;
         mDeltaX = dx;
         mDeltaY = dy;
-        mDurationReciprocal = 1.0f / mDuration;
-        // This controls the viscous fluid effect (how much of it)
-        mViscousFluidScale = 8.0f;
-        // must be set to 1.0 (used in viscousFluid())
-        mViscousFluidNormalize = 1.0f;
-        mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
+        mDurationReciprocal = 1.0f / (float) mDuration;
     }
 
     /**
@@ -393,8 +399,8 @@
         mStartX = startX;
         mStartY = startY;
 
-        mCoeffX = velocity == 0 ? 1.0f : velocityX / velocity; 
-        mCoeffY = velocity == 0 ? 1.0f : velocityY / velocity;
+        float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
+        float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
 
         int totalDistance =
                 (int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
@@ -404,22 +410,20 @@
         mMinY = minY;
         mMaxY = maxY;
 
-        mFinalX = startX + Math.round(totalDistance * mCoeffX);
+        mFinalX = startX + Math.round(totalDistance * coeffX);
         // Pin to mMinX <= mFinalX <= mMaxX
         mFinalX = Math.min(mFinalX, mMaxX);
         mFinalX = Math.max(mFinalX, mMinX);
         
-        mFinalY = startY + Math.round(totalDistance * mCoeffY);
+        mFinalY = startY + Math.round(totalDistance * coeffY);
         // Pin to mMinY <= mFinalY <= mMaxY
         mFinalY = Math.min(mFinalY, mMaxY);
         mFinalY = Math.max(mFinalY, mMinY);
     }
     
-    
-    
-    private float viscousFluid(float x)
+    static float viscousFluid(float x)
     {
-        x *= mViscousFluidScale;
+        x *= sViscousFluidScale;
         if (x < 1.0f) {
             x -= (1.0f - (float)Math.exp(-x));
         } else {
@@ -427,7 +431,7 @@
             x = 1.0f - (float)Math.exp(1.0f - x);
             x = start + x * (1.0f - start);
         }
-        x *= mViscousFluidNormalize;
+        x *= sViscousFluidNormalize;
         return x;
     }
     
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 80a6a2f..78f3cd9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9015,7 +9015,13 @@
     private boolean                 mUserSetTextScaleX;
     private final Paint             mHighlightPaint;
     private int                     mHighlightColor = 0xCC475925;
-    private Layout                  mLayout;
+    /**
+     * This is temporarily visible to fix bug 3085564 in webView. Do not rely on
+     * this field being protected. Will be restored as private when lineHeight
+     * feature request 3215097 is implemented
+     * @hide
+     */
+    protected Layout                mLayout;
 
     private long                    mShowCursor;
     private Blink                   mBlink;
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index ada7f36..1984167 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -120,7 +120,7 @@
     private OnClickListener mSwitchOldListener = new OnClickListener() {
         public void onClick(View v) {
             try {
-                ActivityManagerNative.getDefault().moveTaskToFront(mCurTask);
+                ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0);
             } catch (RemoteException e) {
             }
             finish();
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 63d05ec..125b210 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -32,6 +32,9 @@
     List<InputMethodInfo> getInputMethodList();
     List<InputMethodInfo> getEnabledInputMethodList();
     List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in InputMethodInfo imi);
+    // TODO: We should change the return type from List to List<Parcelable>
+    // Currently there is a bug that aidl doesn't accept List<Parcelable>
+    List getShortcutInputMethodsAndSubtypes();
     void addClient(in IInputMethodClient client,
             in IInputContext inputContext, int uid, int pid);
     void removeClient(in IInputMethodClient client);
diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml
index 3c971da..82d990c 100644
--- a/core/res/res/anim/activity_close_enter.xml
+++ b/core/res/res/anim/activity_close_enter.xml
@@ -18,8 +18,14 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
+		android:zAdjustment="normal"
         android:shareInterpolator="false">
-    <!-- Do nothing. -->
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+    <scale android:fromXScale="0.98" android:toXScale="1.0"
+            android:fromYScale="0.98" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_activityDefaultDur" />
+    <alpha android:fromAlpha=".75" android:toAlpha="1.0"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_activityDefaultDur"/>
 </set>
diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml
index 2001e20..13768b5 100644
--- a/core/res/res/anim/activity_close_exit.xml
+++ b/core/res/res/anim/activity_close_exit.xml
@@ -18,13 +18,15 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
+		android:zAdjustment="top"
         android:shareInterpolator="false">
-    <scale android:fromXScale="1.0" android:toXScale="1.0"
-            android:fromYScale="1.0" android:toYScale="0.9"
+    <scale android:fromXScale="1.0" android:toXScale="1.04"
+            android:fromYScale="1.0" android:toYScale="1.04"
             android:pivotX="50%p" android:pivotY="50%p"
             android:interpolator="@anim/decelerate_quint_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:interpolator="@anim/decelerate_cubic_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+            android:duration="@android:integer/config_activityDefaultDur" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_activityDefaultDur"/>
+
 </set>
diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml
index 6c62e61..4841910 100644
--- a/core/res/res/anim/activity_open_enter.xml
+++ b/core/res/res/anim/activity_open_enter.xml
@@ -18,13 +18,15 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
+		android:zAdjustment="top"
         android:shareInterpolator="false">
-    <scale android:fromXScale="1.0" android:toXScale="1.0"
-            android:fromYScale="0.9" android:toYScale="1.0"
+    <scale android:fromXScale="1.04" android:toXScale="1.0"
+            android:fromYScale="1.04" android:toYScale="1.0"
             android:pivotX="50%p" android:pivotY="50%p"
             android:interpolator="@anim/decelerate_quint_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="0" android:toAlpha="1.0"
-            android:interpolator="@anim/decelerate_cubic_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+            android:duration="@android:integer/config_activityDefaultDur" />
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_activityDefaultDur"/>
+
 </set>
diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml
index 3c971da..81e902f 100644
--- a/core/res/res/anim/activity_open_exit.xml
+++ b/core/res/res/anim/activity_open_exit.xml
@@ -18,8 +18,15 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
+		android:zAdjustment="normal"
         android:shareInterpolator="false">
-    <!-- Do nothing. -->
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+    <scale android:fromXScale="1.0" android:toXScale="0.98"
+            android:fromYScale="1.0" android:toYScale="0.98"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_activityDefaultDur" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.75"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_activityDefaultDur"/>
+
 </set>
diff --git a/core/res/res/anim/dialog_enter.xml b/core/res/res/anim/dialog_enter.xml
index d4983c6..82bfdf6 100644
--- a/core/res/res/anim/dialog_enter.xml
+++ b/core/res/res/anim/dialog_enter.xml
@@ -18,11 +18,13 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
+		android:shareInterpolator="false" >
     <scale android:fromXScale="0.9" android:toXScale="1.0"
            android:fromYScale="0.9" android:toYScale="1.0"
            android:pivotX="50%" android:pivotY="50%"
-           android:duration="@android:integer/config_shortAnimTime" />
+			android:interpolator="@anim/decelerate_quint_interpolator"
+           android:duration="@android:integer/config_activityDefaultDur" />
     <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_shortAnimTime" />
+			android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:duration="@android:integer/config_activityShortDur" />
 </set>
diff --git a/core/res/res/anim/dialog_exit.xml b/core/res/res/anim/dialog_exit.xml
index 2aa629a..829c619 100644
--- a/core/res/res/anim/dialog_exit.xml
+++ b/core/res/res/anim/dialog_exit.xml
@@ -18,11 +18,13 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/accelerate_interpolator">
+		android:shareInterpolator="false" >
     <scale android:fromXScale="1.0" android:toXScale="0.9"
-           android:fromYScale="1.0" android:toYScale="0.9"
-           android:pivotX="50%" android:pivotY="50%"
-           android:duration="@android:integer/config_shortAnimTime" />
+           	android:fromYScale="1.0" android:toYScale="0.9"
+           	android:pivotX="50%" android:pivotY="50%"
+			android:interpolator="@anim/decelerate_quint_interpolator"
+           	android:duration="@android:integer/config_activityDefaultDur" />
     <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:duration="@android:integer/config_shortAnimTime"/>
+			android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:duration="@android:integer/config_activityShortDur"/>
 </set>
diff --git a/core/res/res/anim/fragment_close_enter.xml b/core/res/res/anim/fragment_close_enter.xml
index 1c170e3..c1b564b 100644
--- a/core/res/res/anim/fragment_close_enter.xml
+++ b/core/res/res/anim/fragment_close_enter.xml
@@ -16,12 +16,29 @@
 ** limitations under the License.
 */
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:zAdjustment="normal">
+
+    <objectAnimator
+        android:interpolator="@anim/decelerate_quint_interpolator"
+        android:valueFrom="0.98"
+        android:valueTo="1.0"
+        android:valueType="floatType"
+        android:propertyName="scaleY"
+        android:duration="@android:integer/config_activityDefaultDur"/>
+    <objectAnimator
+        android:interpolator="@anim/decelerate_quint_interpolator"
+        android:valueFrom="0.98"
+        android:valueTo="1.0"
+        android:valueType="floatType"
+        android:propertyName="scaleX"
+        android:duration="@android:integer/config_activityDefaultDur"/>
     <objectAnimator
         android:interpolator="@anim/decelerate_cubic_interpolator"
-        android:valueFrom="0"
-        android:valueTo="1"
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
         android:valueType="floatType"
         android:propertyName="alpha"
-        android:duration="@android:integer/config_mediumAnimTime"/>
+        android:duration="@android:integer/config_activityDefaultDur"/>
+
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_close_exit.xml b/core/res/res/anim/fragment_close_exit.xml
index a084d63..2ce358f 100644
--- a/core/res/res/anim/fragment_close_exit.xml
+++ b/core/res/res/anim/fragment_close_exit.xml
@@ -16,19 +16,28 @@
 ** limitations under the License.
 */
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-    <objectAnimator
-        android:interpolator="@anim/decelerate_cubic_interpolator"
-        android:valueFrom="1"
-        android:valueTo="0"
-        android:valueType="floatType"
-        android:propertyName="alpha"
-        android:duration="@android:integer/config_mediumAnimTime"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:zAdjustment="top">
     <objectAnimator
         android:interpolator="@anim/decelerate_quint_interpolator"
-        android:valueFrom="1"
-        android:valueTo=".8"
+        android:valueFrom="1.0"
+        android:valueTo="1.04"
         android:valueType="floatType"
         android:propertyName="scaleY"
-        android:duration="@android:integer/config_mediumAnimTime"/>
+        android:duration="@android:integer/config_activityDefaultDur"/>
+    <objectAnimator
+        android:interpolator="@anim/decelerate_quint_interpolator"
+        android:valueFrom="1.0"
+        android:valueTo="1.04"
+        android:valueType="floatType"
+        android:propertyName="scaleX"
+        android:duration="@android:integer/config_activityDefaultDur"/>
+    <objectAnimator
+        android:interpolator="@anim/decelerate_cubic_interpolator"
+        android:valueFrom="1.0"
+        android:valueTo="0.0"
+        android:valueType="floatType"
+        android:propertyName="alpha"
+        android:duration="@android:integer/config_activityDefaultDur"/>
+
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_next_enter.xml b/core/res/res/anim/fragment_next_enter.xml
index d3ff7a6..63d3a45 100644
--- a/core/res/res/anim/fragment_next_enter.xml
+++ b/core/res/res/anim/fragment_next_enter.xml
@@ -16,19 +16,13 @@
 ** limitations under the License.
 */
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:zAdjustment="top">
     <objectAnimator
         android:interpolator="@anim/decelerate_cubic_interpolator"
         android:valueFrom="0"
         android:valueTo="1"
         android:valueType="floatType"
         android:propertyName="alpha"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-    <objectAnimator
-        android:interpolator="@anim/decelerate_quint_interpolator"
-        android:valueFrom="50"
-        android:valueTo="0"
-        android:valueType="floatType"
-        android:propertyName="translationY"
-        android:duration="@android:integer/config_mediumAnimTime"/>
+        android:duration="@android:integer/config_activityDefaultDur"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_next_exit.xml b/core/res/res/anim/fragment_next_exit.xml
index e08804f..91e5786 100644
--- a/core/res/res/anim/fragment_next_exit.xml
+++ b/core/res/res/anim/fragment_next_exit.xml
@@ -16,19 +16,13 @@
 ** limitations under the License.
 */
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:zAdjustment="normal">
     <objectAnimator
         android:interpolator="@anim/decelerate_cubic_interpolator"
         android:valueFrom="1"
         android:valueTo="0"
         android:valueType="floatType"
         android:propertyName="alpha"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-    <objectAnimator
-        android:interpolator="@anim/decelerate_quint_interpolator"
-        android:valueFrom="0"
-        android:valueTo="-50"
-        android:valueType="floatType"
-        android:propertyName="translationY"
-        android:duration="@android:integer/config_mediumAnimTime"/>
+        android:duration="@android:integer/config_activityShortDur"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_open_enter.xml b/core/res/res/anim/fragment_open_enter.xml
index 9e625a9..e3bde46 100644
--- a/core/res/res/anim/fragment_open_enter.xml
+++ b/core/res/res/anim/fragment_open_enter.xml
@@ -18,17 +18,25 @@
 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android">
     <objectAnimator
-        android:interpolator="@anim/decelerate_cubic_interpolator"
-        android:valueFrom="0"
-        android:valueTo="1"
-        android:valueType="floatType"
-        android:propertyName="alpha"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-    <objectAnimator
         android:interpolator="@anim/decelerate_quint_interpolator"
-        android:valueFrom=".8"
-        android:valueTo="1"
+        android:valueFrom="1.04"
+        android:valueTo="1.0"
         android:valueType="floatType"
         android:propertyName="scaleY"
-        android:duration="@android:integer/config_mediumAnimTime"/>
+        android:duration="@android:integer/config_activityDefaultDur"/>
+    <objectAnimator
+        android:interpolator="@anim/decelerate_quint_interpolator"
+        android:valueFrom="1.04"
+        android:valueTo="1.0"
+        android:valueType="floatType"
+        android:propertyName="scaleX"
+        android:duration="@android:integer/config_activityDefaultDur"/>
+    <objectAnimator
+        android:interpolator="@anim/decelerate_cubic_interpolator"
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:valueType="floatType"
+        android:propertyName="alpha"
+        android:duration="@android:integer/config_activityDefaultDur"/>
+
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_open_exit.xml b/core/res/res/anim/fragment_open_exit.xml
index d3278b2..b4c2194 100644
--- a/core/res/res/anim/fragment_open_exit.xml
+++ b/core/res/res/anim/fragment_open_exit.xml
@@ -18,10 +18,25 @@
 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android">
     <objectAnimator
+        android:interpolator="@anim/decelerate_quint_interpolator"
+        android:valueFrom="1.0"
+        android:valueTo="0.98"
+        android:valueType="floatType"
+        android:propertyName="scaleY"
+        android:duration="@android:integer/config_activityDefaultDur"/>
+    <objectAnimator
+        android:interpolator="@anim/decelerate_quint_interpolator"
+        android:valueFrom="1.0"
+        android:valueTo="0.98"
+        android:valueType="floatType"
+        android:propertyName="scaleX"
+        android:duration="@android:integer/config_activityDefaultDur"/>
+    <objectAnimator
         android:interpolator="@anim/decelerate_cubic_interpolator"
-        android:valueFrom="1"
-        android:valueTo="0"
+        android:valueFrom="1.0"
+        android:valueTo="0.0"
         android:valueType="floatType"
         android:propertyName="alpha"
-        android:duration="@android:integer/config_mediumAnimTime"/>
+        android:duration="@android:integer/config_activityDefaultDur"/>
+
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_prev_enter.xml b/core/res/res/anim/fragment_prev_enter.xml
index 7ae41e6..63d3a45 100644
--- a/core/res/res/anim/fragment_prev_enter.xml
+++ b/core/res/res/anim/fragment_prev_enter.xml
@@ -16,19 +16,13 @@
 ** limitations under the License.
 */
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:zAdjustment="top">
     <objectAnimator
         android:interpolator="@anim/decelerate_cubic_interpolator"
         android:valueFrom="0"
         android:valueTo="1"
         android:valueType="floatType"
         android:propertyName="alpha"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-    <objectAnimator
-        android:interpolator="@anim/decelerate_quint_interpolator"
-        android:valueFrom="-50"
-        android:valueTo="0"
-        android:valueType="floatType"
-        android:propertyName="translationY"
-        android:duration="@android:integer/config_mediumAnimTime"/>
+        android:duration="@android:integer/config_activityDefaultDur"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_prev_exit.xml b/core/res/res/anim/fragment_prev_exit.xml
index a41f1aa..91e5786 100644
--- a/core/res/res/anim/fragment_prev_exit.xml
+++ b/core/res/res/anim/fragment_prev_exit.xml
@@ -16,19 +16,13 @@
 ** limitations under the License.
 */
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:zAdjustment="normal">
     <objectAnimator
         android:interpolator="@anim/decelerate_cubic_interpolator"
         android:valueFrom="1"
         android:valueTo="0"
         android:valueType="floatType"
         android:propertyName="alpha"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-    <objectAnimator
-        android:interpolator="@anim/decelerate_quint_interpolator"
-        android:valueFrom="0"
-        android:valueTo="50"
-        android:valueType="floatType"
-        android:propertyName="translationY"
-        android:duration="@android:integer/config_mediumAnimTime"/>
+        android:duration="@android:integer/config_activityShortDur"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/grow_fade_in.xml b/core/res/res/anim/grow_fade_in.xml
index 5dc41cb..9180438 100644
--- a/core/res/res/anim/grow_fade_in.xml
+++ b/core/res/res/anim/grow_fade_in.xml
@@ -18,12 +18,13 @@
 */
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-    <scale android:fromXScale="1.0" android:toXScale="1.0"
-           android:fromYScale="0.3" android:toYScale="1.0"
-           android:pivotX="0%" android:pivotY="0%"
-           android:duration="@android:integer/config_shortAnimTime" />
-    <alpha android:interpolator="@anim/decelerate_interpolator"
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+    <scale 	android:interpolator="@anim/decelerate_quint_interpolator"
+	 	   	android:fromXScale="0.9" android:toXScale="1.0"
+           	android:fromYScale="0.9" android:toYScale="1.0"
+           	android:pivotX="50%" android:pivotY="0%"
+           	android:duration="@android:integer/config_activityDefaultDur" />
+    <alpha 	android:interpolator="@anim/decelerate_cubic_interpolator"
             android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_shortAnimTime" />
+            android:duration="@android:integer/config_activityShortDur" />
 </set>
diff --git a/core/res/res/anim/grow_fade_in_center.xml b/core/res/res/anim/grow_fade_in_center.xml
index c07a82e..09370e6 100644
--- a/core/res/res/anim/grow_fade_in_center.xml
+++ b/core/res/res/anim/grow_fade_in_center.xml
@@ -18,12 +18,13 @@
 */
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-    <scale android:fromXScale="0.6" android:toXScale="1.0"
-           android:fromYScale="0.6" android:toYScale="1.0"
-           android:pivotX="50%" android:pivotY="50%"
-           android:duration="@android:integer/config_shortAnimTime" />
-    <alpha android:interpolator="@anim/decelerate_interpolator"
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+    <scale 	android:interpolator="@anim/decelerate_quint_interpolator"
+	 	   	android:fromXScale="0.9" android:toXScale="1.0"
+           	android:fromYScale="0.9" android:toYScale="1.0"
+           	android:pivotX="50%" android:pivotY="50%"
+           	android:duration="@android:integer/config_activityDefaultDur" />
+    <alpha 	android:interpolator="@anim/decelerate_cubic_interpolator"
             android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_shortAnimTime" />
+            android:duration="@android:integer/config_activityShortDur" />
 </set>
diff --git a/core/res/res/anim/grow_fade_in_from_bottom.xml b/core/res/res/anim/grow_fade_in_from_bottom.xml
index 848c677..d1488e9 100644
--- a/core/res/res/anim/grow_fade_in_from_bottom.xml
+++ b/core/res/res/anim/grow_fade_in_from_bottom.xml
@@ -18,12 +18,13 @@
 */
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-    <scale android:fromXScale="0.5" android:toXScale="1.0"
-           android:fromYScale="0.5" android:toYScale="1.0"
-           android:pivotX="0%" android:pivotY="100%"
-           android:duration="@android:integer/config_shortAnimTime" />
-    <alpha android:interpolator="@anim/decelerate_interpolator"
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+    <scale 	android:interpolator="@anim/decelerate_quint_interpolator"
+	 	   	android:fromXScale="0.9" android:toXScale="1.0"
+           	android:fromYScale="0.9" android:toYScale="1.0"
+           	android:pivotX="50%" android:pivotY="100%"
+           	android:duration="@android:integer/config_activityDefaultDur" />
+    <alpha 	android:interpolator="@anim/decelerate_cubic_interpolator"
             android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_shortAnimTime" />
+            android:duration="@android:integer/config_activityShortDur" />
 </set>
diff --git a/core/res/res/anim/input_method_enter.xml b/core/res/res/anim/input_method_enter.xml
index 3858651..e02b310 100644
--- a/core/res/res/anim/input_method_enter.xml
+++ b/core/res/res/anim/input_method_enter.xml
@@ -19,9 +19,11 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
-    <translate android:fromYDelta="20%" android:toYDelta="0"
+		android:shareInterpolator="false">
+    <translate android:fromYDelta="10%" android:toYDelta="0"
+	        android:interpolator="@anim/decelerate_quint_interpolator"
             android:duration="@android:integer/config_shortAnimTime"/>
     <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+			android:interpolator="@anim/decelerate_cubic_interpolator"
             android:duration="@android:integer/config_shortAnimTime" />
 </set>
diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml
index 25369ab..e155f97 100644
--- a/core/res/res/anim/input_method_exit.xml
+++ b/core/res/res/anim/input_method_exit.xml
@@ -18,9 +18,11 @@
 */
 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/accelerate_interpolator">
-    <translate android:fromYDelta="0" android:toYDelta="20%"
+	android:shareInterpolator="false">
+    <translate android:fromYDelta="0" android:toYDelta="10%"
+			android:interpolator="@anim/accelerate_quint_interpolator"
             android:duration="@android:integer/config_shortAnimTime"/>
     <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+			android:interpolator="@anim/accelerate_cubic_interpolator"
             android:duration="@android:integer/config_shortAnimTime"/>
 </set>
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index b243223..0bfe806 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -17,7 +17,7 @@
 */
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/accelerate_interpolator">
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/accelerate_cubic_interpolator">
     <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_longAnimTime" />
+            android:duration="@android:integer/config_activityDefaultDur" />
 </set>
diff --git a/core/res/res/anim/lock_screen_enter.xml b/core/res/res/anim/lock_screen_enter.xml
index dd47ff8..0a169fd 100644
--- a/core/res/res/anim/lock_screen_enter.xml
+++ b/core/res/res/anim/lock_screen_enter.xml
@@ -18,7 +18,7 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
+        android:interpolator="@anim/accelerate_cubic_interpolator">
     <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_mediumAnimTime" />
+            android:duration="@android:integer/config_activityDefaultDur" />
 </set>
diff --git a/core/res/res/anim/lock_screen_exit.xml b/core/res/res/anim/lock_screen_exit.xml
index 077fc6b..f06a6e5 100644
--- a/core/res/res/anim/lock_screen_exit.xml
+++ b/core/res/res/anim/lock_screen_exit.xml
@@ -18,7 +18,7 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/accelerate_interpolator">
+        android:interpolator="@anim/decelerate_cubic_interpolator">
 	<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:duration="@android:integer/config_longAnimTime" />
+            android:duration="@android:integer/config_activityDefaultDur" />
 </set>
diff --git a/core/res/res/anim/shrink_fade_out.xml b/core/res/res/anim/shrink_fade_out.xml
index 4000c23..7229a17 100644
--- a/core/res/res/anim/shrink_fade_out.xml
+++ b/core/res/res/anim/shrink_fade_out.xml
@@ -17,12 +17,14 @@
 ** limitations under the License.
 */
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-    <scale android:fromXScale="1.0" android:toXScale="1.0"
-           android:fromYScale="1.0" android:toYScale="0.3"
-           android:pivotX="0%" android:pivotY="0%"
-           android:duration="@android:integer/config_shortAnimTime" />
-    <alpha android:interpolator="@anim/accelerate_interpolator"
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+    <scale 	android:interpolator="@anim/decelerate_quint_interpolator"
+	    	android:fromXScale="1.0" android:toXScale="0.9"
+	        android:fromYScale="1.0" android:toYScale="0.9"
+           	android:pivotX="50%" android:pivotY="0%"
+           	android:duration="@android:integer/config_activityDefaultDur" />
+    <alpha 	android:interpolator="@anim/decelerate_cubic_interpolator"
             android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:duration="@android:integer/config_shortAnimTime"/>
-</set>
+            android:duration="@android:integer/config_activityShortDur" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/shrink_fade_out_center.xml b/core/res/res/anim/shrink_fade_out_center.xml
index a41731e..23fa0b0 100644
--- a/core/res/res/anim/shrink_fade_out_center.xml
+++ b/core/res/res/anim/shrink_fade_out_center.xml
@@ -17,12 +17,14 @@
 ** limitations under the License.
 */
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-    <scale android:fromXScale="1.0" android:toXScale="0.5"
-           android:fromYScale="1.0" android:toYScale="0.5"
-           android:pivotX="50%" android:pivotY="50%"
-           android:duration="@android:integer/config_shortAnimTime" />
-    <alpha android:interpolator="@anim/accelerate_interpolator"
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+    <scale 	android:interpolator="@anim/decelerate_quint_interpolator"
+	    	android:fromXScale="1.0" android:toXScale="0.9"
+	        android:fromYScale="1.0" android:toYScale="0.9"
+           	android:pivotX="50%" android:pivotY="50%"
+           	android:duration="@android:integer/config_activityDefaultDur" />
+    <alpha 	android:interpolator="@anim/decelerate_cubic_interpolator"
             android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:duration="@android:integer/config_shortAnimTime"/>
-</set>
+            android:duration="@android:integer/config_activityShortDur" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/shrink_fade_out_from_bottom.xml b/core/res/res/anim/shrink_fade_out_from_bottom.xml
index 345a2e061..0639425 100644
--- a/core/res/res/anim/shrink_fade_out_from_bottom.xml
+++ b/core/res/res/anim/shrink_fade_out_from_bottom.xml
@@ -17,12 +17,14 @@
 ** limitations under the License.
 */
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-    <scale android:fromXScale="1.0" android:toXScale="1.0"
-           android:fromYScale="1.0" android:toYScale="0.3"
-           android:pivotX="0%" android:pivotY="100%"
-           android:duration="@android:integer/config_shortAnimTime" />
-    <alpha android:interpolator="@anim/accelerate_interpolator"
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+    <scale 	android:interpolator="@anim/decelerate_quint_interpolator"
+	    	android:fromXScale="1.0" android:toXScale="0.9"
+	        android:fromYScale="1.0" android:toYScale="0.9"
+           	android:pivotX="50%" android:pivotY="100%"
+           	android:duration="@android:integer/config_activityDefaultDur" />
+    <alpha 	android:interpolator="@anim/decelerate_cubic_interpolator"
             android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:duration="@android:integer/config_shortAnimTime"/>
-</set>
+            android:duration="@android:integer/config_activityShortDur" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index 9bec8a3..5057eed 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -19,16 +19,16 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:detachWallpaper="true" android:shareInterpolator="false">
-    <scale android:fromXScale="1.0" android:toXScale="1.0"
-            android:fromYScale="0.9" android:toYScale="1.0"
+    <scale 	android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale="0.95" android:toYScale="1.0"
             android:pivotX="50%p" android:pivotY="50%p"
-            android:fillEnabled="true" android:fillBefore="true"
+			android:fillEnabled="true" android:fillBefore="true"
             android:interpolator="@anim/decelerate_quint_interpolator"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="0" android:toAlpha="1.0"
-            android:fillEnabled="true" android:fillBefore="true"
+            android:startOffset="300"
+            android:duration="300" />
+    <alpha 	android:fromAlpha="0" android:toAlpha="1.0"
             android:interpolator="@anim/decelerate_cubic_interpolator"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+            android:fillEnabled="true" android:fillBefore="true"
+			android:startOffset="300"
+            android:duration="300"/>
 </set>
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index ca60214..169f846 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -18,19 +18,15 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
+        android:detachWallpaper="true" android:shareInterpolator="false">
     <scale android:fromXScale="1.0" android:toXScale="1.0"
-            android:fromYScale="1.0" android:toYScale="0.9"
+			android:fillEnabled="true" android:fillAfter="true"
+            android:fromYScale="1.0" android:toYScale="0.0"
             android:pivotX="50%p" android:pivotY="50%p"
-            android:fillEnabled="true" android:fillAfter="true"
-            android:interpolator="@anim/decelerate_quint_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime" />
+            android:interpolator="@anim/linear_interpolator"
+            android:duration="300" />
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:fillEnabled="true" android:fillAfter="true"
-            android:interpolator="@anim/decelerate_quint_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-    <!-- This is just to keep the animation running for the entire duration. -->
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+			android:fillEnabled="true" android:fillAfter="true"
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:duration="150"/>
 </set>
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 4f72ba2..8f8515a 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -20,15 +20,15 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:shareInterpolator="false">
     <scale android:fromXScale="1.0" android:toXScale="1.0"
-            android:fromYScale="0.9" android:toYScale="1.0"
+            android:fromYScale="0.95" android:toYScale="1.0"
             android:pivotX="50%p" android:pivotY="50%p"
             android:fillEnabled="true" android:fillBefore="true"
             android:interpolator="@anim/decelerate_quint_interpolator"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime" />
+            android:startOffset="300"
+            android:duration="300" />
     <alpha android:fromAlpha="0" android:toAlpha="1.0"
             android:fillEnabled="true" android:fillBefore="true"
-            android:interpolator="@anim/decelerate_cubic_interpolator"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:startOffset="300"
+            android:duration="300"/>
 </set>
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index 6174151..7d2b1b1 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -20,17 +20,13 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:detachWallpaper="true" android:shareInterpolator="false">
     <scale android:fromXScale="1.0" android:toXScale="1.0"
-            android:fromYScale="1.0" android:toYScale="0.9"
+            android:fromYScale="1.0" android:toYScale="0.0"
             android:pivotX="50%p" android:pivotY="50%p"
             android:fillEnabled="true" android:fillAfter="true"
-            android:interpolator="@anim/decelerate_quint_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime" />
+            android:interpolator="@anim/linear_interpolator"
+            android:duration="300" />
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
             android:fillEnabled="true" android:fillAfter="true"
-            android:interpolator="@anim/decelerate_quint_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-    <!-- This is just to keep the animation running for the entire duration. -->
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:duration="150"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/wallpaper_close_enter.xml b/core/res/res/anim/wallpaper_close_enter.xml
index 760acb3..1ce0738 100644
--- a/core/res/res/anim/wallpaper_close_enter.xml
+++ b/core/res/res/anim/wallpaper_close_enter.xml
@@ -17,7 +17,6 @@
 */
 -->
 
-<!-- New holo animation, zooming contents on top of wallpaper down. -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:shareInterpolator="false">
     <scale android:fromXScale="1.0" android:toXScale="1.0"
@@ -25,64 +24,11 @@
             android:pivotX="50%p" android:pivotY="50%p"
             android:fillEnabled="true" android:fillBefore="true"
             android:interpolator="@anim/decelerate_quint_interpolator"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime" />
+            android:startOffset="200"
+            android:duration="300" />
     <alpha android:fromAlpha="0" android:toAlpha="1.0"
             android:fillEnabled="true" android:fillBefore="true"
             android:interpolator="@anim/decelerate_quint_interpolator"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+            android:startOffset="200"
+            android:duration="300"/>
 </set>
-
-<!-- This version zooms the new non-wallpaper up out of the
-     wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:zAdjustment="top">
-    <scale android:fromXScale=".5" android:toXScale="1.0"
-           android:fromYScale=".5" android:toYScale="1.0"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:interpolator="@anim/decelerate_interpolator"
-           android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="0" android:toAlpha="1.0"
-           android:interpolator="@anim/accelerate_decelerate_interpolator"
-           android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
-     wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-    <scale android:fromXScale="2.0" android:toXScale="1.0"
-           android:fromYScale="2.0" android:toYScale="1.0"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
-     wallpaper. -->
-<!-- 
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
-    <scale android:fromXScale="2.0" android:toXScale="1.0"
-           android:fromYScale="2.0" android:toYScale="1.0"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version is a variation on the inter-activity slide that
-    also scales the wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
-	<translate android:fromXDelta="33%" android:toXDelta="0"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
diff --git a/core/res/res/anim/wallpaper_close_exit.xml b/core/res/res/anim/wallpaper_close_exit.xml
index b804916..39af5f9 100644
--- a/core/res/res/anim/wallpaper_close_exit.xml
+++ b/core/res/res/anim/wallpaper_close_exit.xml
@@ -17,77 +17,16 @@
 */
 -->
 
-<!-- New holo animation, zooming contents on top of wallpaper down. -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:detachWallpaper="true" android:shareInterpolator="false">
-    <scale android:fromXScale="1.0" android:toXScale="0.8"
-            android:fromYScale="1.0" android:toYScale="0.8"
+    <scale android:fromXScale="1.0" android:toXScale="0.9"
+            android:fromYScale="1.0" android:toYScale="0.9"
             android:pivotX="50%p" android:pivotY="50%p"
             android:fillEnabled="true" android:fillAfter="true"
             android:interpolator="@anim/decelerate_quint_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime" />
+            android:duration="300" />
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
             android:fillEnabled="true" android:fillAfter="true"
             android:interpolator="@anim/decelerate_cubic_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-    <!-- This is just to keep the animation running for the entire duration. -->
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+            android:duration="150"/>
 </set>
-
-<!-- This version zooms the new non-wallpaper up out of the
-     wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:detachWallpaper="true">
-    <scale android:fromXScale="1.0" android:toXScale="2.0"
-           android:fromYScale="1.0" android:toYScale="2.0"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
-     wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:detachWallpaper="true">
-    <scale android:fromXScale="1.0" android:toXScale=".5"
-           android:fromYScale="1.0" android:toYScale=".5"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
-     wallpaper.  The wallpaper here just stays fixed behind. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-    <scale android:fromXScale="1.0" android:toXScale=".5"
-           android:fromYScale="1.0" android:toYScale=".5"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version is a variation on the inter-activity slide that
-    also scales the wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-    <scale android:fromXScale="1.0" android:toXScale="2.0"
-           android:fromYScale="1.0" android:toYScale="2.0"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-	<translate android:fromXDelta="0%" android:toXDelta="-100%"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
diff --git a/core/res/res/anim/wallpaper_intra_close_enter.xml b/core/res/res/anim/wallpaper_intra_close_enter.xml
index 73bf9a3..1ce0738 100644
--- a/core/res/res/anim/wallpaper_intra_close_enter.xml
+++ b/core/res/res/anim/wallpaper_intra_close_enter.xml
@@ -18,8 +18,17 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
-            android:duration="@android:integer/config_shortAnimTime" />
+        android:shareInterpolator="false">
+    <scale android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale=".9" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:fillEnabled="true" android:fillBefore="true"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:startOffset="200"
+            android:duration="300" />
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:fillEnabled="true" android:fillBefore="true"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:startOffset="200"
+            android:duration="300"/>
 </set>
diff --git a/core/res/res/anim/wallpaper_intra_close_exit.xml b/core/res/res/anim/wallpaper_intra_close_exit.xml
index d43660e..39af5f9 100644
--- a/core/res/res/anim/wallpaper_intra_close_exit.xml
+++ b/core/res/res/anim/wallpaper_intra_close_exit.xml
@@ -18,7 +18,15 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
-    <alpha android:fromAlpha="0.5" android:toAlpha="0.0"
-            android:duration="@android:integer/config_shortAnimTime"/>
+        android:detachWallpaper="true" android:shareInterpolator="false">
+    <scale android:fromXScale="1.0" android:toXScale="0.9"
+            android:fromYScale="1.0" android:toYScale="0.9"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:fillEnabled="true" android:fillAfter="true"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="300" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0"
+            android:fillEnabled="true" android:fillAfter="true"
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:duration="150"/>
 </set>
diff --git a/core/res/res/anim/wallpaper_intra_open_enter.xml b/core/res/res/anim/wallpaper_intra_open_enter.xml
index c85ca4c..0a8b7d2 100644
--- a/core/res/res/anim/wallpaper_intra_open_enter.xml
+++ b/core/res/res/anim/wallpaper_intra_open_enter.xml
@@ -18,7 +18,15 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
-    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
-            android:duration="@android:integer/config_shortAnimTime" />
+        android:detachWallpaper="true" android:shareInterpolator="false">
+    <scale android:fromXScale="0.95" android:toXScale="1.0"
+            android:fromYScale="0.95" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:startOffset="200"
+            android:duration="300" />
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:startOffset="200"
+            android:duration="300"/>
 </set>
diff --git a/core/res/res/anim/wallpaper_intra_open_exit.xml b/core/res/res/anim/wallpaper_intra_open_exit.xml
index eaeac22..d55ee6e 100644
--- a/core/res/res/anim/wallpaper_intra_open_exit.xml
+++ b/core/res/res/anim/wallpaper_intra_open_exit.xml
@@ -18,8 +18,13 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-    <alpha android:fromAlpha="0.5" android:toAlpha="0.0"
-            android:duration="@android:integer/config_shortAnimTime"/>
-</set>
+        android:shareInterpolator="false">
+    <scale android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale="1.0" android:toYScale="0.0"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:interpolator="@anim/linear_interpolator"
+            android:duration="300" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0"
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:duration="160"/>
+</set> 
diff --git a/core/res/res/anim/wallpaper_open_enter.xml b/core/res/res/anim/wallpaper_open_enter.xml
index 72ac671..0a8b7d2 100644
--- a/core/res/res/anim/wallpaper_open_enter.xml
+++ b/core/res/res/anim/wallpaper_open_enter.xml
@@ -17,74 +17,16 @@
 */
 -->
 
-<!-- New holo animation, zooming contents on top of wallpaper back up. -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:detachWallpaper="true" android:shareInterpolator="false">
-    <scale android:fromXScale="0.8" android:toXScale="1.0"
-            android:fromYScale="0.8" android:toYScale="1.0"
+    <scale android:fromXScale="0.95" android:toXScale="1.0"
+            android:fromYScale="0.95" android:toYScale="1.0"
             android:pivotX="50%p" android:pivotY="50%p"
             android:interpolator="@anim/decelerate_quint_interpolator"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime" />
+            android:startOffset="200"
+            android:duration="300" />
     <alpha android:fromAlpha="0" android:toAlpha="1.0"
             android:interpolator="@anim/decelerate_cubic_interpolator"
-            android:startOffset="@android:integer/config_mediumAnimTime"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+            android:startOffset="200"
+            android:duration="300"/>
 </set>
-
-
-<!-- This version zooms the exiting non-wallpaper down in to the
-     wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:detachWallpaper="true">
-    <scale android:fromXScale="2.0" android:toXScale="1.0"
-           android:fromYScale="2.0" android:toYScale="1.0"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
-     wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:detachWallpaper="true">
-    <scale android:fromXScale=".5" android:toXScale="1.0"
-           android:fromYScale=".5" android:toYScale="1.0"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper up off the wallpaper the
-     wallpaper.  The wallpaper here just stays fixed behind. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-    <scale android:fromXScale=".5" android:toXScale="1.0"
-           android:fromYScale=".5" android:toYScale="1.0"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version is a variation on the inter-activity slide that
-    also scales the wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-    <scale android:fromXScale="2.0" android:toXScale="1.0"
-           android:fromYScale="2.0" android:toYScale="1.0"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-	<translate android:fromXDelta="-100%" android:toXDelta="0"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
diff --git a/core/res/res/anim/wallpaper_open_exit.xml b/core/res/res/anim/wallpaper_open_exit.xml
index 7df3563..d55ee6e 100644
--- a/core/res/res/anim/wallpaper_open_exit.xml
+++ b/core/res/res/anim/wallpaper_open_exit.xml
@@ -17,68 +17,14 @@
 */
 -->
 
-<!-- New holo animation, zooming contents on top of wallpaper back up. -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:shareInterpolator="false">
     <scale android:fromXScale="1.0" android:toXScale="1.0"
-            android:fromYScale="1.0" android:toYScale=".9"
+            android:fromYScale="1.0" android:toYScale="0.0"
             android:pivotX="50%p" android:pivotY="50%p"
-            android:interpolator="@anim/decelerate_quint_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime" />
+            android:interpolator="@anim/linear_interpolator"
+            android:duration="300" />
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:interpolator="@anim/decelerate_quint_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
-
-<!-- This version zooms the exiting non-wallpaper down in to the
-     wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:zAdjustment="top">
-    <scale android:fromXScale="1.0" android:toXScale=".5"
-           android:fromYScale="1.0" android:toYScale=".5"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:interpolator="@anim/decelerate_interpolator"
-           android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="1.0" android:toAlpha="0"
-           android:interpolator="@anim/accelerate_decelerate_interpolator"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
-     wallpaper, without zooming the wallpaper itself. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-    <scale android:fromXScale="1.0" android:toXScale="2.0"
-           android:fromYScale="1.0" android:toYScale="2.0"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
-
-<!-- This version zooms the new non-wallpaper down on top of the
-     wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
-    <scale android:fromXScale="1.0" android:toXScale="2.0"
-           android:fromYScale="1.0" android:toYScale="2.0"
-           android:pivotX="50%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-</set>
--->
-
-<!-- This version is a variation on the inter-activity slide that
-    also scales the wallpaper. -->
-<!--
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
-	<translate android:fromXDelta="0%" android:toXDelta="33%"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-</set>
--->
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:duration="160"/>
+</set> 
diff --git a/core/res/res/drawable-mdpi/stat_notify_error.png b/core/res/res/drawable-mdpi/stat_notify_error.png
index 6ced2b7..1275738 100644
--- a/core/res/res/drawable-mdpi/stat_notify_error.png
+++ b/core/res/res/drawable-mdpi/stat_notify_error.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sdcard.png b/core/res/res/drawable-mdpi/stat_notify_sdcard.png
index 23093ac..fc0784d 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sdcard.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png b/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png
index 9abb1c9..93fad38 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png b/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png
index 9880694..cda4546 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
index e9c74b4..517c515 100644
--- a/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
+++ b/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_adb.png b/core/res/res/drawable-mdpi/stat_sys_adb.png
index 12abeda..f0fad76 100644
--- a/core/res/res/drawable-mdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-mdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_0.png b/core/res/res/drawable-mdpi/stat_sys_battery_0.png
index 4a5e99e..e089120 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_10.png b/core/res/res/drawable-mdpi/stat_sys_battery_10.png
deleted file mode 100755
index b789f23..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_battery_10.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_100.png b/core/res/res/drawable-mdpi/stat_sys_battery_100.png
index d280aeb..70d7fa4 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_100.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_15.png b/core/res/res/drawable-mdpi/stat_sys_battery_15.png
new file mode 100644
index 0000000..be04321
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_15.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_20.png b/core/res/res/drawable-mdpi/stat_sys_battery_20.png
deleted file mode 100644
index 009a9fd..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_battery_20.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_28.png b/core/res/res/drawable-mdpi/stat_sys_battery_28.png
new file mode 100644
index 0000000..f634dde
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_28.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_40.png b/core/res/res/drawable-mdpi/stat_sys_battery_40.png
deleted file mode 100644
index 15b57f4..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_battery_40.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_43.png b/core/res/res/drawable-mdpi/stat_sys_battery_43.png
new file mode 100644
index 0000000..f0376bd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_43.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_57.png b/core/res/res/drawable-mdpi/stat_sys_battery_57.png
new file mode 100644
index 0000000..840af66
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_57.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_60.png b/core/res/res/drawable-mdpi/stat_sys_battery_60.png
deleted file mode 100644
index 21078fd..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_battery_60.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_71.png b/core/res/res/drawable-mdpi/stat_sys_battery_71.png
new file mode 100644
index 0000000..04c3569
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_71.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_80.png b/core/res/res/drawable-mdpi/stat_sys_battery_80.png
deleted file mode 100644
index 9268f7b..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_battery_80.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_85.png b/core/res/res/drawable-mdpi/stat_sys_battery_85.png
new file mode 100644
index 0000000..c742da7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_85.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
index ff3cabd..f8011c9 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim1.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim1.png
deleted file mode 100644
index b563701..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim1.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim100.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim100.png
new file mode 100644
index 0000000..499ced9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim100.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim15.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim15.png
new file mode 100644
index 0000000..c921d6a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim15.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim2.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim2.png
deleted file mode 100644
index 904989e..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim2.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim28.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim28.png
new file mode 100644
index 0000000..f882002
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim28.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim3.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim3.png
deleted file mode 100644
index ba011c9..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim3.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim4.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim4.png
deleted file mode 100644
index 4f1c485..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim4.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim43.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim43.png
new file mode 100644
index 0000000..e7d1069
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim43.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim5.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim5.png
deleted file mode 100644
index 4d3396d..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim5.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim57.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim57.png
new file mode 100644
index 0000000..5e0af3d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim57.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim71.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim71.png
new file mode 100644
index 0000000..fb99059
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim71.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim85.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim85.png
new file mode 100644
index 0000000..072f907
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim85.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png b/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
index ed72ebf..3984c46 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim0.png b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
index 69b95cd..01b65e0 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim1.png b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
index 1e18eb5..2b0633f 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim2.png b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
index d7f2312..2295a6d 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim3.png b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
index 83f8d0f..99db712 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim4.png b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
index 9c1bd47..738dc87 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim5.png b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
index 3a81164..00ff542 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
index b7a5978..f6fcc7a 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
index a203e15..5d05381 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
index 4af7630..a8699bc 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
index 1dd76b1..f5ad44a 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
index 36c18bf..b5884f6 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
index 748331f..ae6b8a9 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery.xml b/core/res/res/drawable/stat_sys_battery.xml
index 968595d..1060375 100644
--- a/core/res/res/drawable/stat_sys_battery.xml
+++ b/core/res/res/drawable/stat_sys_battery.xml
@@ -20,11 +20,12 @@
 
 <level-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:maxLevel="4" android:drawable="@android:drawable/stat_sys_battery_0" />
-    <item android:maxLevel="14" android:drawable="@android:drawable/stat_sys_battery_10" />
-    <item android:maxLevel="29" android:drawable="@android:drawable/stat_sys_battery_20" />
-    <item android:maxLevel="49" android:drawable="@android:drawable/stat_sys_battery_40" />
-    <item android:maxLevel="69" android:drawable="@android:drawable/stat_sys_battery_60" />
-    <item android:maxLevel="89" android:drawable="@android:drawable/stat_sys_battery_80" />
+    <item android:maxLevel="15" android:drawable="@android:drawable/stat_sys_battery_15" />
+    <item android:maxLevel="35" android:drawable="@android:drawable/stat_sys_battery_28" />
+    <item android:maxLevel="49" android:drawable="@android:drawable/stat_sys_battery_43" />
+    <item android:maxLevel="60" android:drawable="@android:drawable/stat_sys_battery_57" />
+    <item android:maxLevel="75" android:drawable="@android:drawable/stat_sys_battery_71" />
+    <item android:maxLevel="90" android:drawable="@android:drawable/stat_sys_battery_85" />
     <item android:maxLevel="100" android:drawable="@android:drawable/stat_sys_battery_100" />
 </level-list>
 
diff --git a/core/res/res/drawable/stat_sys_battery_charge.xml b/core/res/res/drawable/stat_sys_battery_charge.xml
index 92d7c4f..a6c4575 100644
--- a/core/res/res/drawable/stat_sys_battery_charge.xml
+++ b/core/res/res/drawable/stat_sys_battery_charge.xml
@@ -1,7 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* //device/apps/common/res/drawable/stat_sys_battery.xml
-**
 ** Copyright 2007, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License"); 
@@ -19,56 +17,14 @@
 -->
 
 <level-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:maxLevel="14">
-        <animation-list
-                xmlns:android="http://schemas.android.com/apk/res/android"
-                android:oneshot="false">
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim0" android:duration="2000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim1" android:duration="1000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim2" android:duration="1000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim3" android:duration="1000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim4" android:duration="1000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim5" android:duration="1000" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="29">
-        <animation-list
-                xmlns:android="http://schemas.android.com/apk/res/android"
-                android:oneshot="false">
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim1" android:duration="2000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim2" android:duration="1000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim3" android:duration="1000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim4" android:duration="1000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim5" android:duration="1000" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="49">
-        <animation-list
-                xmlns:android="http://schemas.android.com/apk/res/android"
-                android:oneshot="false">
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim2" android:duration="2000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim3" android:duration="1000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim4" android:duration="1000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim5" android:duration="1000" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="69">
-        <animation-list
-                xmlns:android="http://schemas.android.com/apk/res/android"
-                android:oneshot="false">
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim3" android:duration="2000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim4" android:duration="1000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim5" android:duration="1000" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="89">
-        <animation-list
-                xmlns:android="http://schemas.android.com/apk/res/android"
-                android:oneshot="false">
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim4" android:duration="2000" />
-            <item android:drawable="@drawable/stat_sys_battery_charge_anim5" android:duration="1000" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="101" android:drawable="@drawable/stat_sys_battery_charge_anim5" />
+    <item android:maxLevel="4" android:drawable="@android:drawable/stat_sys_battery_charge_anim0" />
+    <item android:maxLevel="15" android:drawable="@android:drawable/stat_sys_battery_charge_anim15" />
+    <item android:maxLevel="35" android:drawable="@android:drawable/stat_sys_battery_charge_anim28" />
+    <item android:maxLevel="49" android:drawable="@android:drawable/stat_sys_battery_charge_anim43" />
+    <item android:maxLevel="60" android:drawable="@android:drawable/stat_sys_battery_charge_anim57" />
+    <item android:maxLevel="75" android:drawable="@android:drawable/stat_sys_battery_charge_anim71" />
+    <item android:maxLevel="90" android:drawable="@android:drawable/stat_sys_battery_charge_anim85" />
+    <item android:maxLevel="100" android:drawable="@android:drawable/stat_sys_battery_charge_anim100" />
 </level-list>
 
+
diff --git a/core/res/res/layout-xlarge/status_bar_latest_event_content.xml b/core/res/res/layout-xlarge/status_bar_latest_event_content.xml
index d599154..e4aa270 100644
--- a/core/res/res/layout-xlarge/status_bar_latest_event_content.xml
+++ b/core/res/res/layout-xlarge/status_bar_latest_event_content.xml
@@ -1,23 +1,20 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        >
-
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
     <ImageView android:id="@+id/icon"
         android:layout_width="48dp"
         android:layout_height="64dp"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
+        android:layout_marginLeft="4dp"
         android:scaleType="center"
         />
-
     <LinearLayout
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:layout_weight="1"
         android:orientation="vertical"
-        android:paddingLeft="16dp"
+        android:paddingLeft="8dp"
         >
         <TextView android:id="@+id/title"
             android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
@@ -37,5 +34,13 @@
             android:fadingEdge="horizontal"
             />
     </LinearLayout>
+    <TextView android:id="@+id/info"
+        android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:singleLine="true"
+        android:gravity="center_vertical"
+        android:paddingLeft="8dp"
+        />
 </LinearLayout>
 
diff --git a/core/res/res/layout-xlarge/status_bar_latest_event_content_large_icon.xml b/core/res/res/layout-xlarge/status_bar_latest_event_content_large_icon.xml
new file mode 100644
index 0000000..1e1f9de
--- /dev/null
+++ b/core/res/res/layout-xlarge/status_bar_latest_event_content_large_icon.xml
@@ -0,0 +1,44 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        >
+        <TextView android:id="@+id/title"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            />
+        <TextView android:id="@+id/text"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            />
+    </LinearLayout>
+    <TextView android:id="@+id/info"
+        android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:singleLine="true"
+        android:gravity="center_vertical"
+        android:paddingLeft="8dp"
+        />
+    <ImageView android:id="@+id/icon"
+        android:layout_width="48dp"
+        android:layout_height="64dp"
+        android:scaleType="center"
+        />
+</LinearLayout>
+
diff --git a/core/res/res/layout-xlarge/status_bar_latest_event_ticker.xml b/core/res/res/layout-xlarge/status_bar_latest_event_ticker.xml
new file mode 100644
index 0000000..b09ed44
--- /dev/null
+++ b/core/res/res/layout-xlarge/status_bar_latest_event_ticker.xml
@@ -0,0 +1,44 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/status_bar_height"
+    >
+    <ImageView android:id="@+id/icon"
+        android:layout_width="48dp"
+        android:layout_height="match_parent"
+        android:scaleType="center"
+        />
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingLeft="16dp"
+        >
+        <TextView android:id="@+id/title"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            />
+        <TextView android:id="@+id/text"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            />
+    </LinearLayout>
+    <TextView android:id="@+id/info"
+        android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:singleLine="true"
+        android:gravity="center_vertical"
+        />
+</LinearLayout>
+
diff --git a/core/res/res/layout-xlarge/status_bar_latest_event_ticker_large_icon.xml b/core/res/res/layout-xlarge/status_bar_latest_event_ticker_large_icon.xml
new file mode 100644
index 0000000..6c2e6f7
--- /dev/null
+++ b/core/res/res/layout-xlarge/status_bar_latest_event_ticker_large_icon.xml
@@ -0,0 +1,44 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingLeft="16dp"
+        >
+        <TextView android:id="@+id/title"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            />
+        <TextView android:id="@+id/text"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            />
+    </LinearLayout>
+    <TextView android:id="@+id/info"
+        android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:singleLine="true"
+        android:gravity="center_vertical"
+        />
+    <ImageView android:id="@+id/icon"
+        android:layout_width="48dp"
+        android:layout_height="match_parent"
+        android:scaleType="center"
+        />
+</LinearLayout>
+
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index 3982ed9..e3ba634 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -81,7 +81,8 @@
             android:paddingTop="2dip"
             android:paddingBottom="12dip"
             android:paddingLeft="14dip"
-            android:paddingRight="10dip">
+            android:paddingRight="10dip"
+            android:overScrollMode="ifContentScrolls">
             <TextView android:id="@+id/message"
                 style="?android:attr/textAppearanceMedium"
                 android:layout_width="match_parent"
diff --git a/core/res/res/layout/preference_dialog_edittext.xml b/core/res/res/layout/preference_dialog_edittext.xml
index 691ee8c..40b9e69 100644
--- a/core/res/res/layout/preference_dialog_edittext.xml
+++ b/core/res/res/layout/preference_dialog_edittext.xml
@@ -19,7 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_marginTop="48dp"
-    android:layout_marginBottom="48dp">
+    android:layout_marginBottom="48dp"
+    android:overScrollMode="ifContentScrolls">
 
     <LinearLayout
         android:id="@+android:id/edittext_container"
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index 6c0e773..0f84418 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -56,7 +56,7 @@
 
         </LinearLayout>
 
-        <FrameLayout android:id="@+id/prefs"
+        <android.preference.PreferenceFrameLayout android:id="@+id/prefs"
                 android:layout_width="0px"
                 android:layout_height="match_parent"
                 android:layout_weight="20"
@@ -66,8 +66,6 @@
                 android:layout_marginBottom="16dp"
                 android:paddingLeft="32dip"
                 android:paddingRight="32dip"
-                android:paddingTop="48dip"
-                android:paddingBottom="48dip"
                 android:background="?attr/preferencePanelBackground"
                 android:visibility="gone" />
     </LinearLayout>
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index c499b8a..dbe0df0 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -18,6 +18,7 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/default_preference_layout"
     android:orientation="vertical"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
@@ -27,6 +28,9 @@
         android:layout_width="match_parent"
         android:layout_height="0px"
         android:layout_weight="1"
+        android:paddingTop="48dip"
+        android:paddingBottom="48dip"
+        android:clipToPadding="false"
         android:drawSelectorOnTop="false"
         android:cacheColorHint="@android:color/transparent"
         android:scrollbarAlwaysDrawVerticalTrack="true" />
diff --git a/core/res/res/layout/select_dialog.xml b/core/res/res/layout/select_dialog.xml
index 94dcb6a..80d22f6 100644
--- a/core/res/res/layout/select_dialog.xml
+++ b/core/res/res/layout/select_dialog.xml
@@ -31,4 +31,5 @@
     android:layout_marginTop="5px"
     android:cacheColorHint="@null"
     android:divider="?android:attr/listDividerAlertDialog"
-    android:scrollbars="vertical" />
+    android:scrollbars="vertical"
+    android:overScrollMode="ifContentScrolls" />
diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-xlarge/config.xml
index 9504d04..37f59c8 100644
--- a/core/res/res/values-xlarge/config.xml
+++ b/core/res/res/values-xlarge/config.xml
@@ -30,8 +30,8 @@
     <!-- Enable lockscreen rotation -->
     <bool name="config_enableLockScreenRotation">true</bool>
 
-    <!-- Enables 3d task switcher on xlarge device -->
-    <bool name="config_enableRecentApps3D">true</bool>
+    <!-- see comment in values/config.xml -->
+    <integer name="config_longPressOnHomeBehavior">1</integer>
 
 </resources>
 
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index f52c627..fef3c13 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -21,6 +21,9 @@
     <dimen name="status_bar_height">48dip</dimen>
     <!-- Height of the status bar -->
     <dimen name="status_bar_icon_size">48dip</dimen>
+    <!-- Size of the giant number (unread count) in the notifications -->
+    <dimen name="status_bar_content_number_size">48sp</dimen>
+    
     <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
     <!-- Margin for permanent screen decorations at the bottom. -->
     <dimen name="screen_margin_bottom">48dip</dimen>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 651bfea..3f81a89 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -610,6 +610,9 @@
 
         <!-- Specifies a drawable to use for the 'home as up' indicator. -->
         <attr name="homeAsUpIndicator" format="reference" />
+
+        <!-- Preference frame layout styles. -->
+        <attr name="preferenceFrameLayoutStyle" format="reference" />
     </declare-styleable>
 
     <!-- **************************************************************** -->
@@ -1632,6 +1635,20 @@
              <code>public void sayHello(View v)</code> method of your context
              (typically, your Activity). -->
         <attr name="onClick" format="string" />
+
+        <!-- Defines over-scrolling behavior. This property is used only if the
+             View is scrollable. Over-scrolling is the ability for the user to
+             receive feedback when attempting to scroll beyond meaningful content. -->
+        <attr name="overScrollMode">
+            <!-- Always show over-scroll effects, even if the content fits entirely
+                 within the available space. -->
+            <enum name="always" value="0" />
+            <!-- Only show over-scroll effects if the content is large
+                 enough to meaningfully scroll. -->
+            <enum name="ifContentScrolls" value="1" />
+            <!-- Never show over-scroll effects. -->
+            <enum name="never" value="2" />
+        </attr>
     </declare-styleable>
 
     <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
@@ -2116,6 +2133,16 @@
         <!-- When set to false, the ListView will not draw the divider before each footer view.
              The default value is true. -->
         <attr name="footerDividersEnabled" format="boolean" />
+        <!-- Drawable to draw above list content. -->
+        <attr name="overScrollHeader" format="reference|color" />
+        <!-- Drawable to draw below list content. -->
+        <attr name="overScrollFooter" format="reference|color" />
+    </declare-styleable>
+    <declare-styleable name="PreferenceFrameLayout">
+        <!-- Padding to use at the top of the prefs content. -->
+        <attr name="topPadding" format="dimension" />
+        <!-- Padding to use at the bottom of the prefs content. -->
+        <attr name="bottomPadding" format="dimension" />
     </declare-styleable>
     <declare-styleable name="MenuView">
         <!-- Default appearance of menu item text. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ac15be6..15b5db4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -66,6 +66,16 @@
     <!-- The duration (in milliseconds) of a long animation. -->
     <integer name="config_longAnimTime">500</integer>
 
+    <!-- The duration (in milliseconds) of the activity open/close and fragment open/close animations. -->
+    <integer name="config_activityShortDur">150</integer>
+    <integer name="config_activityDefaultDur">220</integer>
+    
+    <!-- Duration for the dim animation behind a dialog.  This may be either
+         a percentage, which is relative to the duration of the enter/open
+         animation of the window being shown that is dimming behind, or it may
+         be an integer for a constant duration. -->
+    <fraction name="config_dimBehindFadeDuration">100%</fraction>
+
     <!-- The duration (in milliseconds) that the radio will scan for a signal
          when there's no network connection. If the scan doesn't timeout, use zero -->
     <integer name="config_radioScanningTimeout">0</integer>
@@ -336,8 +346,12 @@
     <!-- Diable lockscreen rotation by default -->
     <bool name="config_enableLockScreenRotation">false</bool>
 
-    <!-- Enable 3D RecentApplications view -->
-    <bool name="config_enableRecentApps3D">false</bool>
+    <!-- Control the behavior when the user long presses the power button.
+            0 - Nothing
+            1 - Recent apps dialog
+            2 - Recent apps activity in SystemUI
+    -->
+    <integer name="config_longPressOnHomeBehavior">0</integer>
 
     <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
          The N entries of this array define N + 1 zones as follows:
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f4d1df8..f2ab5cd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1253,6 +1253,9 @@
   <public type="attr" name="logo" id="0x010102be" />
   <public type="attr" name="xlargeScreens" id="0x010102bf" />
   <public type="attr" name="immersive" id="0x010102c0" />
+  <public type="attr" name="overScrollMode" id="0x010102c1" />
+  <public type="attr" name="overScrollHeader" id="0x010102c2" />
+  <public type="attr" name="overScrollFooter" id="0x010102c3" />
   <public type="attr" name="filterTouchesWhenObscured" id="0x010102c4" />
   <public type="attr" name="textSelectHandleLeft" id="0x010102c5" />
   <public type="attr" name="textSelectHandleRight" id="0x010102c6" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c8a5de8..0c3361d 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1824,8 +1824,10 @@
     <!-- Toast for double-tap -->
     <string name="double_tap_toast">Tip: double-tap to zoom in and out.</string>
 
-    <!-- Text to show in the auto complete drop down list on a text view when the browser can auto fill the entire form -->
+    <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form, and the user has configured an AutoFill profile [CHAR-LIMIT=8] -->
     <string name="autofill_this_form">AutoFill</string>
+    <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=16] -->
+    <string name="setup_autofill">Setup AutoFill</string>
 
     <!-- String used to separate FirstName and LastName when writing out a local name
          e.g. John<separator>Smith [CHAR-LIMIT=NONE]-->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 403ec2f..6985de6 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -48,10 +48,15 @@
         <item name="bottomMedium">@android:drawable/popup_bottom_medium</item>
         <item name="centerMedium">@android:drawable/popup_center_medium</item>
     </style>
-    
+
+    <style name="Widget.PreferenceFrameLayout">
+        <item name="android:topPadding">0dip</item>
+        <item name="android:bottomPadding">0dip</item>
+    </style>
+
     <!-- Base style for animations.  This style specifies no animations. -->
     <style name="Animation" />
-    
+
     <!-- Standard animations for a full-screen window or activity. -->
     <style name="Animation.Activity">
         <item name="activityOpenEnterAnimation">@anim/activity_open_enter</item>
@@ -213,12 +218,17 @@
         <item name="android:textStyle">bold</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent">
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
         <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent.Title">
         <item name="android:textSize">18sp</item>
         <item name="android:textStyle">bold</item>
     </style>
+    <style name="TextAppearance.StatusBar.EventContent.Info">
+        <item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
+        <item name="android:textColor">#ff272727</item>
+    </style>
 
     <!-- Widget Styles -->
 
@@ -1803,7 +1813,7 @@
         <item name="android:background">@null</item>
         <item name="android:homeAsUpIndicator">@android:drawable/ic_ab_back_holo_light</item>
         <item name="android:progressBarStyle">@android:style/Widget.Holo.Light.ProgressBar.Horizontal</item>
-        <item name="android:indeterminateProgressStyle">@android:style/Widget.Holo.Light.ProgressBar.Small</item>
+        <item name="android:indeterminateProgressStyle">@android:style/Widget.Holo.Light.ProgressBar</item>
     </style>
 
     <!-- Animation Styles -->
@@ -1869,4 +1879,8 @@
         <item name="android:textAppearance">@style/TextAppearance.Holo.Light.DialogWindowTitle</item>
     </style>
 
+    <style name="Widget.Holo.PreferenceFrameLayout">
+        <item name="android:topPadding">48dip</item>
+        <item name="android:bottomPadding">48dip</item>
+    </style>
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 9215bf2..1ef99d0 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -262,13 +262,16 @@
 
         <!-- SearchView attributes -->
         <item name="searchDropdownBackground">@android:drawable/search_dropdown_dark</item>
+
+        <!-- PreferenceFrameLayout attributes -->
+        <item name="preferenceFrameLayoutStyle">@android:style/Widget.PreferenceFrameLayout</item>
     </style>
-    
+
     <!-- Variant of the default (dark) theme with no title bar -->
     <style name="Theme.NoTitleBar">
         <item name="android:windowNoTitle">true</item>
     </style>
-    
+
     <!-- Variant of the default (dark) theme that has no title bar and
          fills the entire screen -->
     <style name="Theme.NoTitleBar.Fullscreen">
@@ -871,6 +874,9 @@
 
         <!-- SearchView attributes -->
         <item name="searchDropdownBackground">@android:drawable/search_dropdown_dark</item>
+
+        <!-- PreferenceFrameLayout attributes -->
+        <item name="preferenceFrameLayoutStyle">@android:style/Widget.Holo.PreferenceFrameLayout</item>
     </style>
 
     <!-- New Honeycomb holographic theme. Light version.  The widgets in the
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
index d298d40..b116bea 100644
--- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml
+++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -53,6 +53,15 @@
         android.label="Test runner for unit tests"
     />
 
+    <!-- run stress test suite:
+      "adb shell am instrument -e stressnum <200> -w
+      com.android.connectivitymanagertest/.ConnectivityManagerStressTestRunner"
+    -->
+    <instrumentation android:name=".ConnectivityManagerStressTestRunner"
+        android:targetPackage="com.android.connectivitymanagertest"
+        android:label="Test runner for Connectivity Manager Stress Tests"
+    />
+
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
new file mode 100644
index 0000000..47f208a
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.connectivitymanagertest;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import android.util.Log;
+import com.android.connectivitymanagertest.stress.WifiApStress;
+
+import junit.framework.TestSuite;
+
+/**
+ * Instrumentation Test Runner for all stress tests
+ *
+ * To run the stress tests:
+ *
+ * adb shell am instrument -e stressnum <stress times> \
+ *     -w com.android.connectivitymanagertest/.ConnectivityManagerStressTestRunner
+ */
+
+public class ConnectivityManagerStressTestRunner extends InstrumentationTestRunner {
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(WifiApStress.class);
+        return suite;
+    }
+
+    @Override
+    public ClassLoader getLoader() {
+        return ConnectivityManagerTestRunner.class.getClassLoader();
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        String stressValue = (String) icicle.get("stressnum");
+        if (stressValue != null) {
+            int iteration = Integer.parseInt(stressValue);
+            if (iteration > 0) {
+                numStress = iteration;
+            }
+        }
+    }
+
+    public int numStress = 100;
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index 7c46e7a..37b9f52 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -55,6 +55,9 @@
     public static final int WIFI_SCAN_TIMEOUT = 20 * 1000;
     public static final int SHORT_TIMEOUT = 5 * 1000;
     public static final long LONG_TIMEOUT = 50 * 1000;
+    public static final int SUCCESS = 0;  // for Wifi tethering state change
+    public static final int FAILURE = 1;
+    public static final int INIT = -1;
     private static final String ACCESS_POINT_FILE = "accesspoints.xml";
     public ConnectivityReceiver mConnectivityReceiver = null;
     public WifiReceiver mWifiReceiver = null;
@@ -87,6 +90,10 @@
     public static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
     NetworkState[] connectivityState = new NetworkState[NUM_NETWORK_TYPES];
 
+    // For wifi tethering tests
+    private String[] mWifiRegexs;
+    public int mWifiTetherResult = INIT;    // -1 is initialization state
+
     /**
      * A wrapper of a broadcast receiver which provides network connectivity information
      * for all kinds of network: wifi, mobile, etc.
@@ -150,6 +157,16 @@
                 mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                                                 WifiManager.WIFI_STATE_UNKNOWN);
                 notifyWifiState();
+            } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
+                notifyWifiAPState();
+            } else if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
+                ArrayList<String> available = intent.getStringArrayListExtra(
+                        ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+                ArrayList<String> active = intent.getStringArrayListExtra(
+                        ConnectivityManager.EXTRA_ACTIVE_TETHER);
+                ArrayList<String> errored = intent.getStringArrayListExtra(
+                        ConnectivityManager.EXTRA_ERRORED_TETHER);
+                updateTetherState(available.toArray(), active.toArray(), errored.toArray());
             }
             else {
                 return;
@@ -184,6 +201,8 @@
         mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+        mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+        mIntentFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
         registerReceiver(mWifiReceiver, mIntentFilter);
 
         // Get an instance of ConnectivityManager
@@ -196,6 +215,7 @@
             Log.v(LOG_TAG, "Clear Wifi before we start the test.");
             removeConfiguredNetworksAndDisableWifi();
         }
+        mWifiRegexs = mCM.getTetherableWifiRegexs();
      }
 
     public List<WifiConfiguration> loadNetworkConfigurations() throws Exception {
@@ -263,13 +283,57 @@
         }
     }
 
-    public void notifyWifiState() {
+    private void notifyWifiState() {
         synchronized (wifiObject) {
             Log.v(LOG_TAG, "notify wifi state changed");
             wifiObject.notify();
         }
     }
 
+    private void notifyWifiAPState() {
+        synchronized (this) {
+            Log.v(LOG_TAG, "notify wifi AP state changed");
+            this.notify();
+        }
+    }
+
+    // Update wifi tethering state
+    private void updateTetherState(Object[] available, Object[] tethered, Object[] errored) {
+        boolean wifiTethered = false;
+        boolean wifiErrored = false;
+
+        synchronized (this) {
+            for (Object obj: tethered) {
+                String str = (String)obj;
+                for (String tethRex: mWifiRegexs) {
+                    Log.v(LOG_TAG, "str: " + str +"tethRex: " + tethRex);
+                    if (str.matches(tethRex)) {
+                        wifiTethered = true;
+                    }
+                }
+            }
+
+            for (Object obj: errored) {
+                String str = (String)obj;
+                for (String tethRex: mWifiRegexs) {
+                    Log.v(LOG_TAG, "error: str: " + str +"tethRex: " + tethRex);
+                    if (str.matches(tethRex)) {
+                        wifiErrored = true;
+                    }
+                }
+            }
+
+            if (wifiTethered) {
+                mWifiTetherResult = SUCCESS;   // wifi tethering is successful
+            } else if (wifiErrored) {
+                mWifiTetherResult = FAILURE;   // wifi tethering failed
+            }
+            Log.v(LOG_TAG, "mWifiTetherResult: " + mWifiTetherResult);
+            this.notify();
+        }
+    }
+
+
     // Wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED,
     //                                      DISCONNECTING, DISCONNECTED, UNKNOWN
     public boolean waitForNetworkState(int networkType, State expectedState, long timeout) {
@@ -332,6 +396,62 @@
         }
     }
 
+    // Wait for Wifi AP state: WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING,
+    //                         WIFI_AP_STATE_ENABLED, WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
+    public boolean waitForWifiAPState(int expectedState, long timeout) {
+        long startTime = System.currentTimeMillis();
+        while (true) {
+            if ((System.currentTimeMillis() - startTime) > timeout) {
+                if (mWifiManager.getWifiApState() != expectedState) {
+                    return false;
+                } else {
+                    return true;
+                }
+            }
+            Log.v(LOG_TAG, "Wait for wifi AP state to be: " + expectedState);
+            synchronized (wifiObject) {
+                try {
+                    wifiObject.wait(SHORT_TIMEOUT);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                if (mWifiManager.getWifiApState() != expectedState) {
+                    Log.v(LOG_TAG, "Wifi state is: " + mWifiManager.getWifiApState());
+                    continue;
+                }
+                return true;
+            }
+        }
+    }
+
+    /**
+     * Wait for the wifi tethering result:
+     * @param timeout is the maximum waiting time
+     * @return SUCCESS if tethering result is successful
+     *         FAILURE if tethering result returns error.
+     */
+    public int waitForTetherStateChange(long timeout) {
+        long startTime = System.currentTimeMillis();
+        while (true) {
+            if ((System.currentTimeMillis() - startTime) > timeout) {
+                return mWifiTetherResult;
+            }
+            Log.v(LOG_TAG, "Wait for wifi tethering result.");
+            synchronized (this) {
+                try {
+                    this.wait(SHORT_TIMEOUT);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                if (mWifiTetherResult == INIT ) {
+                    continue;
+                } else {
+                    return mWifiTetherResult;
+                }
+            }
+        }
+    }
+
     // Return true if device is currently connected to mobile network
     public boolean isConnectedToMobile() {
         return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
new file mode 100644
index 0000000..cc53ddc
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.connectivitymanagertest.stress;
+
+
+import com.android.connectivitymanagertest.ConnectivityManagerStressTestRunner;
+import com.android.connectivitymanagertest.ConnectivityManagerTestActivity;
+import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+/**
+ * Stress the wifi driver as access point.
+ */
+public class WifiApStress
+    extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
+    private final static String TAG = "WifiApStress";
+    private ConnectivityManagerTestActivity mAct;
+    private static String NETWORK_ID = "AndroidAPTest";
+    private static String PASSWD = "androidwifi";
+    private int max_num;
+
+    public WifiApStress() {
+        super(ConnectivityManagerTestActivity.class);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mAct = getActivity();
+        max_num = ((ConnectivityManagerStressTestRunner)getInstrumentation()).numStress;
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @LargeTest
+    public void testWifiHotSpot() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = NETWORK_ID;
+        config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+        config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
+        config.preSharedKey = PASSWD;
+
+        // If Wifi is enabled, disable it
+        if (mAct.mWifiManager.isWifiEnabled()) {
+            mAct.disableWifi();
+        }
+        for (int i = 0; i < max_num; i++) {
+            Log.v(TAG, "iteration: " + i);
+            // enable Wifi tethering
+            assertTrue(mAct.mWifiManager.setWifiApEnabled(config, true));
+            // Wait for wifi ap state to be ENABLED
+            assertTrue(mAct.waitForWifiAPState(mAct.mWifiManager.WIFI_AP_STATE_ENABLED,
+                    mAct.LONG_TIMEOUT));
+            // Wait for wifi tethering result
+            assertEquals(mAct.SUCCESS, mAct.waitForTetherStateChange(2*mAct.SHORT_TIMEOUT));
+            // Allow the wifi tethering to be enabled for 10 seconds
+            try {
+                Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
+            } catch (Exception e) {
+                fail("thread in sleep is interrupted");
+            }
+            assertTrue(mAct.mWifiManager.setWifiApEnabled(config, false));
+        }
+    }
+
+}
diff --git a/data/sounds/AudioPackage5.mk b/data/sounds/AudioPackage5.mk
new file mode 100755
index 0000000..550f990
--- /dev/null
+++ b/data/sounds/AudioPackage5.mk
@@ -0,0 +1,72 @@
+#
+# Audio Package 5 - Crespo/Soju
+# 
+# Include this file in a product makefile to include these audio files
+#
+# 
+
+LOCAL_PATH:= frameworks/base/data/sounds
+
+PRODUCT_COPY_FILES += \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \
+	$(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \
+	$(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \
+	$(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \
+	$(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
+	$(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
+	$(LOCAL_PATH)/notifications/Capella.ogg:system/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \
+	$(LOCAL_PATH)/notifications/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
+	$(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \
+	$(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \
+	$(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \
+	$(LOCAL_PATH)/notifications/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \
+	$(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \
+	$(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \
+	$(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \
+	$(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \
+	$(LOCAL_PATH)/ringtones/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \
+	$(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \
+	$(LOCAL_PATH)/ringtones/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \
+	$(LOCAL_PATH)/ringtones/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
+	$(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \
+	$(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
+	$(LOCAL_PATH)/ringtones/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \
+	$(LOCAL_PATH)/ringtones/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
+	$(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \
+	$(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg
diff --git a/data/sounds/notifications/Aldebaran.ogg b/data/sounds/notifications/Aldebaran.ogg
new file mode 100755
index 0000000..fe30137
--- /dev/null
+++ b/data/sounds/notifications/Aldebaran.ogg
Binary files differ
diff --git a/data/sounds/notifications/Altair.ogg b/data/sounds/notifications/Altair.ogg
new file mode 100644
index 0000000..660c800
--- /dev/null
+++ b/data/sounds/notifications/Altair.ogg
Binary files differ
diff --git a/data/sounds/notifications/Antares.ogg b/data/sounds/notifications/Antares.ogg
new file mode 100755
index 0000000..f4f94d7
--- /dev/null
+++ b/data/sounds/notifications/Antares.ogg
Binary files differ
diff --git a/data/sounds/notifications/Betelgeuse.ogg b/data/sounds/notifications/Betelgeuse.ogg
new file mode 100755
index 0000000..7145a16
--- /dev/null
+++ b/data/sounds/notifications/Betelgeuse.ogg
Binary files differ
diff --git a/data/sounds/notifications/Canopus.ogg b/data/sounds/notifications/Canopus.ogg
new file mode 100755
index 0000000..0d55925
--- /dev/null
+++ b/data/sounds/notifications/Canopus.ogg
Binary files differ
diff --git a/data/sounds/notifications/Capella.ogg b/data/sounds/notifications/Capella.ogg
new file mode 100644
index 0000000..ae4f3c5
--- /dev/null
+++ b/data/sounds/notifications/Capella.ogg
Binary files differ
diff --git a/data/sounds/notifications/Castor.ogg b/data/sounds/notifications/Castor.ogg
new file mode 100644
index 0000000..92de8e7
--- /dev/null
+++ b/data/sounds/notifications/Castor.ogg
Binary files differ
diff --git a/data/sounds/notifications/CetiAlpha.ogg b/data/sounds/notifications/CetiAlpha.ogg
new file mode 100755
index 0000000..cd09526
--- /dev/null
+++ b/data/sounds/notifications/CetiAlpha.ogg
Binary files differ
diff --git a/data/sounds/notifications/CetiAlpha.wav b/data/sounds/notifications/CetiAlpha.wav
new file mode 100755
index 0000000..d209645
--- /dev/null
+++ b/data/sounds/notifications/CetiAlpha.wav
Binary files differ
diff --git a/data/sounds/notifications/Deneb.ogg b/data/sounds/notifications/Deneb.ogg
new file mode 100644
index 0000000..3b17e28
--- /dev/null
+++ b/data/sounds/notifications/Deneb.ogg
Binary files differ
diff --git a/data/sounds/notifications/Electra.ogg b/data/sounds/notifications/Electra.ogg
new file mode 100644
index 0000000..9335d8d
--- /dev/null
+++ b/data/sounds/notifications/Electra.ogg
Binary files differ
diff --git a/data/sounds/notifications/Fomalhaut.ogg b/data/sounds/notifications/Fomalhaut.ogg
new file mode 100644
index 0000000..9448c18
--- /dev/null
+++ b/data/sounds/notifications/Fomalhaut.ogg
Binary files differ
diff --git a/data/sounds/notifications/Merope.ogg b/data/sounds/notifications/Merope.ogg
new file mode 100755
index 0000000..fb18c73
--- /dev/null
+++ b/data/sounds/notifications/Merope.ogg
Binary files differ
diff --git a/data/sounds/notifications/Polaris.ogg b/data/sounds/notifications/Polaris.ogg
new file mode 100644
index 0000000..d5b991f
--- /dev/null
+++ b/data/sounds/notifications/Polaris.ogg
Binary files differ
diff --git a/data/sounds/notifications/Pollux.ogg b/data/sounds/notifications/Pollux.ogg
new file mode 100644
index 0000000..d37c75c
--- /dev/null
+++ b/data/sounds/notifications/Pollux.ogg
Binary files differ
diff --git a/data/sounds/notifications/Procyon.ogg b/data/sounds/notifications/Procyon.ogg
new file mode 100644
index 0000000..93d1557
--- /dev/null
+++ b/data/sounds/notifications/Procyon.ogg
Binary files differ
diff --git a/data/sounds/notifications/Sirrah.ogg b/data/sounds/notifications/Sirrah.ogg
new file mode 100755
index 0000000..ee79e49
--- /dev/null
+++ b/data/sounds/notifications/Sirrah.ogg
Binary files differ
diff --git a/data/sounds/notifications/arcturus.ogg b/data/sounds/notifications/arcturus.ogg
new file mode 100644
index 0000000..9d73103
--- /dev/null
+++ b/data/sounds/notifications/arcturus.ogg
Binary files differ
diff --git a/data/sounds/notifications/regulus.ogg b/data/sounds/notifications/regulus.ogg
new file mode 100644
index 0000000..4f28d9e
--- /dev/null
+++ b/data/sounds/notifications/regulus.ogg
Binary files differ
diff --git a/data/sounds/notifications/sirius.ogg b/data/sounds/notifications/sirius.ogg
new file mode 100644
index 0000000..78c9991
--- /dev/null
+++ b/data/sounds/notifications/sirius.ogg
Binary files differ
diff --git a/data/sounds/notifications/vega.ogg b/data/sounds/notifications/vega.ogg
new file mode 100644
index 0000000..e596e60
--- /dev/null
+++ b/data/sounds/notifications/vega.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ANDROMEDA.ogg b/data/sounds/ringtones/ANDROMEDA.ogg
new file mode 100644
index 0000000..8f6bd3e
--- /dev/null
+++ b/data/sounds/ringtones/ANDROMEDA.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Aquila.ogg b/data/sounds/ringtones/Aquila.ogg
new file mode 100644
index 0000000..b391be9
--- /dev/null
+++ b/data/sounds/ringtones/Aquila.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ArgoNavis.ogg b/data/sounds/ringtones/ArgoNavis.ogg
new file mode 100644
index 0000000..b4202ac
--- /dev/null
+++ b/data/sounds/ringtones/ArgoNavis.ogg
Binary files differ
diff --git a/data/sounds/ringtones/BOOTES.ogg b/data/sounds/ringtones/BOOTES.ogg
new file mode 100644
index 0000000..0716a4f
--- /dev/null
+++ b/data/sounds/ringtones/BOOTES.ogg
Binary files differ
diff --git a/data/sounds/ringtones/CANISMAJOR.ogg b/data/sounds/ringtones/CANISMAJOR.ogg
new file mode 100644
index 0000000..177d3de
--- /dev/null
+++ b/data/sounds/ringtones/CANISMAJOR.ogg
Binary files differ
diff --git a/data/sounds/ringtones/CASSIOPEIA.ogg b/data/sounds/ringtones/CASSIOPEIA.ogg
new file mode 100644
index 0000000..c4a7948
--- /dev/null
+++ b/data/sounds/ringtones/CASSIOPEIA.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Carina.ogg b/data/sounds/ringtones/Carina.ogg
new file mode 100644
index 0000000..aeb9b36
--- /dev/null
+++ b/data/sounds/ringtones/Carina.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Carina.wav b/data/sounds/ringtones/Carina.wav
new file mode 100755
index 0000000..ecaeb58
--- /dev/null
+++ b/data/sounds/ringtones/Carina.wav
Binary files differ
diff --git a/data/sounds/ringtones/Centaurus.ogg b/data/sounds/ringtones/Centaurus.ogg
new file mode 100644
index 0000000..404bdbc
--- /dev/null
+++ b/data/sounds/ringtones/Centaurus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Cygnus.ogg b/data/sounds/ringtones/Cygnus.ogg
new file mode 100644
index 0000000..b2e1e65
--- /dev/null
+++ b/data/sounds/ringtones/Cygnus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Draco.ogg b/data/sounds/ringtones/Draco.ogg
new file mode 100644
index 0000000..88d5a04
--- /dev/null
+++ b/data/sounds/ringtones/Draco.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Eridani.ogg b/data/sounds/ringtones/Eridani.ogg
new file mode 100644
index 0000000..b665a29
--- /dev/null
+++ b/data/sounds/ringtones/Eridani.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Lyra.ogg b/data/sounds/ringtones/Lyra.ogg
new file mode 100644
index 0000000..696f278
--- /dev/null
+++ b/data/sounds/ringtones/Lyra.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Machina.ogg b/data/sounds/ringtones/Machina.ogg
new file mode 100644
index 0000000..ac16f7e
--- /dev/null
+++ b/data/sounds/ringtones/Machina.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Orion.ogg b/data/sounds/ringtones/Orion.ogg
new file mode 100644
index 0000000..807f107
--- /dev/null
+++ b/data/sounds/ringtones/Orion.ogg
Binary files differ
diff --git a/data/sounds/ringtones/PERSEUS.ogg b/data/sounds/ringtones/PERSEUS.ogg
new file mode 100644
index 0000000..ad06021
--- /dev/null
+++ b/data/sounds/ringtones/PERSEUS.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Pegasus.ogg b/data/sounds/ringtones/Pegasus.ogg
new file mode 100644
index 0000000..66c4970
--- /dev/null
+++ b/data/sounds/ringtones/Pegasus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Pyxis.ogg b/data/sounds/ringtones/Pyxis.ogg
new file mode 100644
index 0000000..2d3adce
--- /dev/null
+++ b/data/sounds/ringtones/Pyxis.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Rigel.ogg b/data/sounds/ringtones/Rigel.ogg
new file mode 100644
index 0000000..af2c176
--- /dev/null
+++ b/data/sounds/ringtones/Rigel.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Scarabaeus.ogg b/data/sounds/ringtones/Scarabaeus.ogg
new file mode 100644
index 0000000..e70fc69
--- /dev/null
+++ b/data/sounds/ringtones/Scarabaeus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Sceptrum.ogg b/data/sounds/ringtones/Sceptrum.ogg
new file mode 100644
index 0000000..fc50aef
--- /dev/null
+++ b/data/sounds/ringtones/Sceptrum.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Solarium.ogg b/data/sounds/ringtones/Solarium.ogg
new file mode 100644
index 0000000..d27f141
--- /dev/null
+++ b/data/sounds/ringtones/Solarium.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Testudo.ogg b/data/sounds/ringtones/Testudo.ogg
new file mode 100644
index 0000000..0ca8d6b
--- /dev/null
+++ b/data/sounds/ringtones/Testudo.ogg
Binary files differ
diff --git a/data/sounds/ringtones/URSAMINOR.ogg b/data/sounds/ringtones/URSAMINOR.ogg
new file mode 100644
index 0000000..c0010e82
--- /dev/null
+++ b/data/sounds/ringtones/URSAMINOR.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Vespa.ogg b/data/sounds/ringtones/Vespa.ogg
new file mode 100644
index 0000000..4423bbb
--- /dev/null
+++ b/data/sounds/ringtones/Vespa.ogg
Binary files differ
diff --git a/data/sounds/ringtones/hydra.ogg b/data/sounds/ringtones/hydra.ogg
new file mode 100644
index 0000000..edde14f
--- /dev/null
+++ b/data/sounds/ringtones/hydra.ogg
Binary files differ
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 3cc1806..2548128 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -71,6 +71,9 @@
           <li><a href="<?cs var:toroot ?>guide/topics/ui/menus.html">
                <span class="en">Creating Menus</span>
               </a></li>
+          <li><a href="<?cs var:toroot ?>guide/topics/ui/actionbar.html">
+               <span class="en">Using the Action Bar</span>
+              </a> <span class="new">new!</span></li>
           <li><a href="<?cs var:toroot ?>guide/topics/ui/dialogs.html">
                 <span class="en">Creating Dialogs</span>
               </a></li>
diff --git a/docs/html/guide/topics/fragments/index.jd b/docs/html/guide/topics/fragments/index.jd
index ce10ef7..766146e 100644
--- a/docs/html/guide/topics/fragments/index.jd
+++ b/docs/html/guide/topics/fragments/index.jd
@@ -399,7 +399,7 @@
 Fragment newFragment = new MyFragment();
 FragmentTransaction ft = openFragmentTransaction();
 // Replace and add to back stack
-ft.replace(newFragment, R.id.myfragment);
+ft.replace(R.id.myfragment, newFragment);
 ft.addToBackStack(null);
 // Apply changes
 ft.commit();
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index 84c2ed2..fffc1cd 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -19,6 +19,7 @@
     <li><a href="#lmodes">Launch modes</a></li>
     <li><a href="#clearstack">Clearing the stack</a></li>
     <li><a href="#starttask">Starting tasks</a></li>
+    <li><a href="#commonpatterns">Common patterns</a></li>
   </ol></li>
 <li><a href="#procthread">Processes and Threads</a>
   <ol>
@@ -892,6 +893,60 @@
 </p>
 
 
+<h3 id="commonpatterns">Common patterns</h3>
+
+<p>
+In most cases an application won't use any flags or special features.
+This gives the standard interaction the user expects: launching the application
+brings any existing task to the foreground, or starts the main activity in
+a new task if there isn't one.
+</p>
+
+<p>
+If an application posts notifications, it needs to decide how the user's
+selection of a notification should impact any currently running task.  The
+current suggested behavior is that any current tasks be completely removed,
+replaced with a new task containing a stack of activities representing where
+the user is jumping in to the app.  This can be accomplished with a combination
+of the {@link android.app.PendingIntent#getActivities PendingIntent.getActivities()}
+method and {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK Intent.FLAG_ACTIVITY_CLEAR_TASK}.
+</p>
+
+<p>
+For example, here is sample code to build an array of Intents to launch an
+application into an activity three levels deep.  The first Intent is always
+the main Intent of the application as started by the launcher.  The exact
+details of the Intent data will of course depend on your application.
+</p>
+
+{@sample development/samples/ApiDemos/src/com/example/android/apis/app/StatusBarNotifications.java
+      intent_array}
+
+<p>
+In some cases an application may not want to directly launch its application
+from a notification, but instead go to a intermediate summary activity.  To
+accomplish this, the summary activity should be given a task affinity that
+is different from the main application (one will typically give it no affinity,
+that is "") so that it does not get launched into any existing application task.
+</p>
+
+{@sample development/samples/ApiDemos/AndroidManifest.xml no_task_affinity}
+
+<p>
+The PendingIntent to launch this then does not need to supply anything special:
+</p>
+
+{@sample development/samples/ApiDemos/src/com/example/android/apis/app/IncomingMessage.java
+      pending_intent}
+
+<p>
+If an application implements an app widget, it should generally use the same
+approach as the first one for notifications: when the user taps on the app
+widget it should throw away any current task of the application and start a
+new task with potentially multiple activities representing the state the
+user is jumping in to.
+</p>
+
 <h2 id="procthread">Processes and Threads</h2>
 
 <p>
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
new file mode 100644
index 0000000..2b942e7
--- /dev/null
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -0,0 +1,332 @@
+page.title=Using the Action Bar
+parent.title=User Interface
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+  <h2>Quickview</h2>
+  <ul>
+    <li>A replacement for the title bar for displaying global actions for the activity</li>
+    <li>Provides toolbar actions and modes of navigating around the application</li>
+    <li>Switches to contextual menu options when one or more items are selected</li>
+    <li>Requires API Level HONEYCOMB</li>
+  </ul>
+
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#Adding">Adding the Action Bar</a></li>
+    <li><a href="#ActionItems">Adding Action Items</a>
+      <ol>
+        <li><a href="#Home">Using the application icon as an action item</a></li>
+      </ol>
+    </li>
+    <li><a href="#Tabs">Adding Tabs</a></li>
+    <li><a href="#Dropdown">Adding Drop-down Navigation</a></li>
+    <li><a href="#Search">Adding Search</a></li>
+  </ol>
+
+  <h2>Key classes</h2>
+  <ol>
+    <li>{@link android.app.ActionBar}</li>
+    <li>{@link android.view.Menu}</li>
+  </ol>
+
+  <h2>See also</h2>
+  <ol>
+    <li><a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a></li>
+  </ol>
+</div>
+</div>
+
+<p>The action bar is a widget for activities that replaces the traditional title bar at
+the top of an activity. By default, the action bar includes the application logo on the left side,
+followed by the activity title. The action bar offers several useful features for
+applications&mdash;especially those targeted to tablet devices. The action bar features include
+the ability to:</p>
+
+<ul>
+  <li>Display menu items from the <a
+href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">options menu</a> as "action
+items"&mdash;providing instant access to key user actions.</li>
+  <li>Provide tabs for navigating between <a
+href="{@docRoot}guide/topics/fragments/index.html">fragments</a>.</li>
+  <li>Provide drop-down navigation items.</li>
+  <li>Embed a {@link android.widget.SearchView} for instant searching.</li>
+  <li>Use the application logo as a "return home" or "up" navigation action.</li>
+</ul>
+
+<img src="{@docRoot}images/ui/actionbar.png" height="36" alt="" />
+<p class="img-caption"><strong>Figure 1.</strong> A screenshot of the action bar in the NotePad
+sample application, containing action items to save and delete the note.</p>
+
+
+<h2 id="Adding">Adding the Action Bar</h2>
+
+<p>To add the Action Bar to your activity, apply the holographic theme&mdash;{@code
+Theme.Holo}&mdash;or the action bar theme&mdash;{@code Theme.WithActionBar}&mdash;in your manifest
+file. For example:</p>
+
+<pre>
+&lt;activity android:theme="@android:style/Theme.Holo" &gt;
+</pre>
+
+<p>The activity now appears with the action bar in place of the traditional title bar.</p>
+
+
+<h2 id="ActionItems">Adding Action Items</h2>
+
+<p>For each action item you want to add to the action bar, you must add a menu item to the
+activity's <a href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">options menu</a> and declare
+that the item be shown as an action, using the {@code android:showAsAction} attribute in the menu
+XML or with {@link android.view.MenuItem#setShowAsAction setShowAsAction()} on the {@link
+android.view.MenuItem}.</p>
+
+<div class="figure" style="width:359px">
+  <img src="{@docRoot}images/ui/actionbar-item-withtext.png" height="57" alt="" />
+  <p class="img-caption"><strong>Figure 2.</strong> A screenshot from an action bar with two
+action items.</p>
+</div>
+
+<p>You can specify a menu item to appear as an action item in the action bar&mdash;if there is room
+for it&mdash;from the <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu
+resource</a> by declaring {@code
+android:showAsAction="ifRoom"} for the {@code &lt;item&gt;} element. This way, the item will display
+in the action bar for quick access only if there is room available for it&mdash;if there's not
+enough room, the item is placed the options menu (revealed by the drop-down icon on the right side
+of the action bar). From your application code, you can specify the item to appear as an action item
+by calling {@link android.view.MenuItem#setShowAsAction setShowAsAction()} on the {@link
+android.view.MenuItem} and passing {@link android.view.MenuItem#SHOW_AS_ACTION_IF_ROOM}.</p>
+
+<p>If your item supplies both a title and an icon, then the action item shows only
+the icon by defult. If you want to include the text with the action item, add the <em>with text</em>
+flag&mdash;in XML, add {@code withText} to the {@code android:showAsAction} attribute or, in
+your application code, use the {@link android.view.MenuItem#SHOW_AS_ACTION_WITH_TEXT} flag when
+calling {@link android.view.MenuItem#setShowAsAction setShowAsAction()}. Figure 2 shows a screenshot
+of an action bar with two action items that include text.</p>
+
+<p>Here's an example of how you can declare a menu item as an action item in a <a
+href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a> file:</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android">
+    &lt;item android:id="@+id/menu_add"
+          android:icon="@drawable/ic_menu_save"
+          android:title="@string/menu_save"
+          <b>android:showAsAction="ifRoom|withText"</b> /&gt;
+&lt;/menu&gt;
+</pre>
+
+<p>In this case, both the {@code ifRoom} and {@code withText} flags are set, so that when this
+item appears as an action item, it includes the title text along with the icon.</p>
+
+<p>A menu item placed in the action bar triggers the same callback methods as other items in the
+options menu. When the user selects an item in the action bar, your activity receives a call to
+{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}, passing the
+item ID. (If you added the item from a fragment, then the respective {@link
+android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method is called
+for that fragment.)</p>
+
+<p class="note"><strong>Note:</strong> Even menu items that are contained in the options menu
+(and not shown as action items) will show an icon, so when using the action bar, you should
+provide an icon for every item in the options menu.</p>
+
+<p>You can also declare an item to <em>always</em> appear as an action item,  but you should avoid
+doing so. Most of the time, there will be enough room for several action items and they will appear
+in the order you declare them. If you set items to always appear as action
+items  (instead of <em>if room</em>), then they are added without discrimination and there is a risk
+that they will collide with other elements in the action bar, such as tabs or custom views.</p>
+
+<p>For more information about menus, see the <a
+href="{@docRoot}guide/topics/ui/menus.html#options-menu">Creating Menus</a> developer guide.</p>
+
+
+<h3 id="Home">Using the application icon as an action item</h3>
+
+<p>By default, the application icon appears in the action bar on the left side, but does nothing
+when tapped. To use the application icon as an action item when tapped, you simply need to add a
+condition to your {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} method
+that performs an action when the {@link android.view.MenuItem} ID is {@code android.R.id.home}.
+This ID is delivered every time the user taps the application icon.</p>
+
+<p>For example, here's an implementation of {@link android.app.Activity#onOptionsItemSelected
+onOptionsItemSelected()} that returns to the application's "home" activity:</p>
+
+<pre>
+&#64;Override
+public boolean onOptionsItemSelected(MenuItem item) {
+    switch (item.getItemId()) {
+        case android.R.id.home:
+            // app icon in action bar clicked; go home
+            Intent intent = new Intent(this, HomeActivity.class);
+            startActivity(intent);
+            break;
+    }
+    return super.onOptionsItemSelected(item);
+}
+</pre>
+
+<div class="figure" style="width:144px">
+  <img src="{@docRoot}images/ui/actionbar-logo.png" height="140" alt="" />
+  <p class="img-caption"><strong>Figure 3.</strong> The standard icon for the Email application
+(top) and the "up navigation" icon (bottom).</p>
+</div>
+
+<p>You can also use the application icon to provide "up" navigation. The way you handle the event
+when a user taps the icon is the same, but if the user experience for the event is to <em>navigate
+up to the parent activity</em>, then you should indicate this behavior by setting the
+action bar to "show home as up." You can do so by calling {@link
+android.app.ActionBar#setDisplayOptions setDisplayOptions()} on your activity's {@link
+android.app.ActionBar}, and passing the {@link
+android.app.ActionBar#DISPLAY_HOME_AS_UP} display option.</p>
+
+<p>To get the {@link android.app.ActionBar}, call {@link android.app.Activity#getActionBar} from
+your {@link android.app.Activity} during {@link android.app.Activity#onCreate onCreate()} (but be
+sure you do so <em>after</em> you've called {@link android.app.Activity#setContentView
+setContentView()}).</p>
+
+<p>For example, here's how you can change the action bar display mode to show the application
+icon as an "up" action:</p>
+
+<pre>
+&#64;Override
+protected void onStart() {
+  super.onStart();
+  ActionBar actionBar = this.getActionBar();
+  actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
+}
+</pre>
+
+<p class="caution"><strong>Caution:</strong> If your activity does not have an action bar (if you
+did not set the theme of your activity or application to the holographic or action bar theme), then
+{@link android.app.Activity#getActionBar} returns null.</p>
+
+
+<h2 id="Tabs">Adding Tabs</h2>
+
+<p>The action bar can display tabs that allow the user navigate between different fragments in the
+activity. Each tab can include a title and/or an icon.</p>
+
+<p>To begin, your layout must include a {@link android.view.View} in which each {@link
+android.app.Fragment} associated with a tab is displayed. Be sure the view has an ID that you
+can use to reference it from your code.</p>
+
+<p>To add tabs to the action bar:</p>
+<ol>
+  <li>Create an implementation of {@link android.app.ActionBar.TabListener} to handle the
+interaction events on the action bar tabs. You must implement all methods: {@link
+android.app.ActionBar.TabListener#onTabSelected onTabSelected()}, {@link
+android.app.ActionBar.TabListener#onTabUnselected onTabUnselected()}, and {@link
+android.app.ActionBar.TabListener#onTabReselected onTabReselected()}.
+    <p>Each callback method passes the {@link android.app.ActionBar.Tab} that received the
+event and a {@link android.app.FragmentTransaction} for you to perform the fragment
+transactions (add or remove fragments).</p>
+    <p>For example:</p>
+<pre>
+private class MyTabListener implements ActionBar.TabListener {
+    private TabContentFragment mFragment;
+
+    // Called to create an instance of the listener when adding a new tab
+    public TabListener(TabContentFragment fragment) {
+        mFragment = fragment;
+    }
+
+    &#64;Override
+    public void onTabSelected(Tab tab, FragmentTransaction ft) {
+        ft.add(R.id.fragment_content, mFragment, null);
+    }
+
+    &#64;Override
+    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+        ft.remove(mFragment);
+    }
+
+    &#64;Override
+    public void onTabReselected(Tab tab, FragmentTransaction ft) {
+        // do nothing
+    }
+
+}
+</pre>
+  <p>This implementation of {@link android.app.ActionBar.TabListener} adds a constructor
+that saves the {@link android.app.Fragment} associated with a tab so that each callback
+can add or remove that fragment.</p>
+  </li>
+  <li>Get the {@link android.app.ActionBar} for your activity by calling {@link
+android.app.Activity#getActionBar} from your {@link android.app.Activity}, during {@link
+android.app.Activity#onCreate onCreate()} (but be sure you do so <em>after</em> you've called
+{@link android.app.Activity#setContentView setContentView()}).</li>
+  <li>Call {@link android.app.ActionBar#setNavigationMode(int)
+setNavigationMode(NAVIGATION_MODE_TABS)} to enable tab mode for the {@link
+android.app.ActionBar}.</li>
+  <li>Create each tab for the action bar:
+    <ol>
+      <li>Create a new {@link android.app.ActionBar.Tab} by calling {@link
+android.app.ActionBar#newTab()} on the {@link android.app.ActionBar}.</li>
+      <li>Add title text and/or an icon for the tab by calling {@link
+android.app.ActionBar.Tab#setText setText()} and/or {@link android.app.ActionBar.Tab#setIcon
+setIcon()}.
+        <p class="note"><strong>Tip:</strong> These methods return the same {@link
+android.app.ActionBar.Tab} instance, so you can chain the calls together.</p></li>
+      <li>Declare the {@link android.app.ActionBar.TabListener} to use for the tab by passing an
+instance of your implementation to {@link android.app.ActionBar.Tab#setTabListener
+setTabListener()}.
+    </ol>
+  </li>
+  <li>Add each {@link android.app.ActionBar.Tab} to the action bar by calling {@link
+android.app.ActionBar#addTab addTab()} on the {@link android.app.ActionBar} and passing the
+{@link android.app.ActionBar.Tab}.</li>
+</ol>
+<p>For example, the following code combines steps 2 - 5 to create two tabs and add them to
+the action bar:</p>
+<pre>
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.main);
+
+    // setup action bar for tabs
+    final ActionBar actionBar = getActionBar();
+    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+    // remove the activity title to make space for tabs
+    actionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
+
+    // instantiate fragment for the tab
+    Fragment artistsFragment = new ArtistsFragment();
+    // add a new tab and set its title text and tab listener
+    bar.addTab(bar.newTab().setText(R.string.tab_artists)
+            .setTabListener(new TabListener(artistsFragment)));
+
+    Fragment albumsFragment = new AlbumsFragment();
+    bar.addTab(bar.newTab().setText(R.string.tab_albums)
+            .setTabListener(new TabListener(albumsFragment)));
+}
+</pre>
+
+<p>All the behaviors that occur when a tab is selected must be defined by your {@link
+android.app.ActionBar.TabListener} callback methods. When a tab is selected, it receives a call to
+{@link android.app.ActionBar.TabListener#onTabSelected onTabSelected()} and that's where you should
+add the appropriate fragment to the designated view in your layout, using {@link
+android.app.FragmentTransaction#add add()} with the provided {@link
+android.app.FragmentTransaction}. Likewise, when a tab is deselected (because another tab becomes
+selected), you should remove that fragment from the layout, using {@link
+android.app.FragmentTransaction#remove remove()}.</p>
+
+<p class="note"><strong>Note:</strong> You <strong>do not</strong> need
+to call {@link android.app.FragmentTransaction#commit} for these transactions. You also
+<strong>cannot</strong> add these fragment transactions to the back stack.</p>
+
+<p>If your activity is stopped, you should retain the currently selected tab with the saved state so
+that when the user returns to your application, you can open the tab. When it's time to save the
+state, you can query the currently selected tab with {@link
+android.app.ActionBar#getSelectedNavigationItem()}. This returns the index position of the selected
+tab.</p>
+
+<p class="caution"><strong>Caution:</strong> It's important that you save
+the state of each fragment as necessary, so when the user switches fragments with the tabs,
+then returns to a previous fragment, it appears the way they left. For information about saving
+the state of your fragment, see the <a
+href="{@docRoot}guide/topics/fragments/index.html">Fragments</a> developer guide.</p>
+
+
diff --git a/docs/html/guide/topics/ui/declaring-layout.jd b/docs/html/guide/topics/ui/declaring-layout.jd
index fe641a2..4d71d28 100644
--- a/docs/html/guide/topics/ui/declaring-layout.jd
+++ b/docs/html/guide/topics/ui/declaring-layout.jd
@@ -191,10 +191,12 @@
 <p>Every ViewGroup class implements a nested class that extends {@link
 android.view.ViewGroup.LayoutParams}. This subclass
 contains property types that define the size and position for each child view, as
-appropriate for the view group. As you can see in the figure below, the parent
+appropriate for the view group. As you can see in figure 1, the parent
 view group defines layout parameters for each child view (including the child view group).</p>
 
 <img src="{@docRoot}images/layoutparams.png" alt="" height="300" align="center"/>
+<p class="img-caption"><strong>Figure 1.</strong> Visualization of a view hierarchy with layout
+parameters associated with each view.</p>
 
 <p>Note that every LayoutParams subclass has its own syntax for setting
 values. Each child element must define LayoutParams that are appropriate for its parent, 
diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd
index 879eb8b..d50e1cb 100644
--- a/docs/html/guide/topics/ui/dialogs.jd
+++ b/docs/html/guide/topics/ui/dialogs.jd
@@ -22,20 +22,32 @@
       </li>
       <li><a href="#CustomDialog">Creating a Custom Dialog</a></li>
     </ol>
-    
+
     <h2>Key classes</h2>
     <ol>
       <li>{@link android.app.Dialog}</li>
+      <li>{@link android.app.AlertDialog}</li>
+      <li>{@link android.app.DialogFragment}</li>
+    </ol>
+
+    <h2>Related tutorials</h2>
+    <ol>
+      <li><a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Hello
+DatePicker</a></li>
+      <li><a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Hello
+TimePicker</a></li>
     </ol>
   </div>
 </div>
 
 <p>A dialog is usually a small window that appears in front of the current Activity.
-The underlying Activity loses focus and the dialog accepts all user interaction. 
-Dialogs are normally used
-for notifications and short activities that directly relate to the application in progress.</p>
+The underlying Activity loses focus and the dialog accepts all user interaction. Dialogs are
+normally used for notifications that should interupt the user and to perform short tasks that
+directly relate to the application in progress (such as a progress bar or a login prompt).</p>
 
-<p>The Android API supports the following types of {@link android.app.Dialog} objects:</p>
+<p>The {@link android.app.Dialog} class is the base class for creating dialogs. However, you
+typically should not instantiate a {@link android.app.Dialog} directly. Instead, you should use one
+of the following subclasses:</p>
 <dl>
   <dt>{@link android.app.AlertDialog}</dt>
   <dd>A dialog that can manage zero, one, two, or three buttons, and/or a list of
diff --git a/docs/html/guide/topics/ui/menus.jd b/docs/html/guide/topics/ui/menus.jd
index b4e467c..ce25b9f 100644
--- a/docs/html/guide/topics/ui/menus.jd
+++ b/docs/html/guide/topics/ui/menus.jd
@@ -36,6 +36,7 @@
 
   <h2>See also</h2>
   <ol>
+    <li><a href="{@docRoot}guide/topics/ui/actionbar.html">Using the Action Bar</a></li>
     <li><a href="{@docRoot}guide/topics/resources/menu-resource.html">Menu Resource</a></li>
   </ol>
 </div>
@@ -48,20 +49,9 @@
 <p>Android provides three types of application menus:</p>
 <dl>
   <dt><strong>Options Menu</strong></dt>
-    <dd>The primary menu for an Activity, which appears when the user presses
-    the device MENU key. Within the Options Menu are two groups:
-      <dl style="margin-top:1em">
-        <dt><em>Icon Menu</em></dt>
-          <dd>The menu items visible at the bottom of the screen
-          at the press of the MENU key. It supports a maximum of six menu items.
-          These are the only menu items that support icons and the only menu items that <em>do not</em> support
-          checkboxes or radio buttons.</dd>
-        <dt><em>Expanded Menu</em></dt>
-          <dd>The vertical list of menu items exposed by the "More" menu item in the Icon Menu.
-          When the Icon Menu is full, the expanded menu is comprised of the sixth
-          menu item and the rest.</dd>
-      </dl>
-    </dd>
+    <dd>The primary collection of menu items for an Activity that is associated with the device MENU
+key. To provide instant access to select menu items, you can place some items in the <a
+href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a>, if available.</dd>
   <dt><strong>Context Menu</strong></dt>
     <dd>A floating list of menu items that appears when the user performs a long-press on a View.
 </dd>
@@ -74,7 +64,7 @@
 
 <h2 id="xml">Defining Menus</h2>
 
-<p>Instead of instantiating {@link android.view.Menu} objects in your application code, you should
+<p>Instead of instantiating a {@link android.view.Menu} in your application code, you should
 define a menu and all its items in an XML <a
 href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>, then inflate the menu
 resource (load it as a programmable object) in your application code. Defining your menus in XML is
@@ -104,9 +94,9 @@
     &lt;item android:id="@+id/new_game"
           android:icon="@drawable/ic_new_game"
           android:title="@string/new_game" /&gt;
-    &lt;item android:id="@+id/quit"
-          android:icon="@drawable/ic_quit"
-          android:title="@string/quit" /&gt;
+    &lt;item android:id="@+id/help"
+          android:icon="@drawable/ic_help"
+          android:title="@string/help" /&gt;
 &lt;/menu&gt;
 </pre>
 
@@ -161,36 +151,64 @@
 </div>
 
 
-<p>The Options Menu is where you should include basic application functions
-and necessary navigation items (for example, a button
-to open application settings). The user
-can open the Options Menu with the device MENU key.
-Figure 1 shows a screenshot of an Options Menu.</p>
+<p>The Options Menu is where you should include basic application functions and necessary navigation
+items (for example, a button to open the application settings). Items in the Options Menu are
+accessible in two distinct ways: in the Action Bar and in the menu revealed by the MENU
+key.</p>
 
-<p>When opened, the first visible portion of the Options Menu is called the Icon Menu. It
-holds the first six menu items.
-If you add more than six items to the Options Menu, Android places the sixth item and those after it
-into the Expanded Menu, which the user can open with the "More" menu item.</p>
+<p>The Action Bar is an optional widget that appears at the top of the activity in place of the
+title bar. It can display several menu items that you choose from the Options Menu, but items in
+the Action Bar display only an icon (no title text). Users can reveal the other menu items in the
+Options Menu with the MENU key.</p>
+
+<p>If you include the Action Bar in your activity, the menu items that are not placed in the Action
+Bar can appear in two different styles:</p>
+<dl>
+  <dt>Action Bar Menu</dt>
+    <dd>If the device has an extra-large screen ({@code xlarge}), then all items in the Options Menu
+that are not placed in the Action Bar are placed into a drop-down list at the right side of the
+Action Bar, with icons and title text. The user can reveal the drop-down list by pressing the
+drop-down icon in the Action Bar or the MENU key.</dd>
+  <dt>Standard Options Menu</dt>
+    <dd>If the device <em>does not</em> have an extra-large screen, then all items in the Options
+Menu that are not placed in the Action Bar are placed into the Standard Options Menu at the bottom
+of the activity. The user can reveal the standard Options Menu by pressing the MENU key.
+    <p>The first visible portion of the Standard Options Menu is called the Icon Menu.
+It holds the first six menu items (excluding any added to the Action Bar), with icons and title
+text. If there are more than six items, Android adds a "More" item as the sixth menu item and places
+the remaining items into the Expanded Menu, which the user can open by selecting "More". The
+Expanded Menu displays menu items only by their title text (no icon)</p>
+    </dd>
+</dl>
 
 <p>When the user opens the Options Menu for the first time, Android calls your Activity's
 {@link android.app.Activity#onCreateOptionsMenu(Menu)
 onCreateOptionsMenu()} method. Override this method in your Activity
 and populate the {@link android.view.Menu} that is passed into the method. Populate the
 {@link android.view.Menu} by inflating a menu resource as described in <a
-href="#Inflating">Inflating a Menu Resource</a>. (You can
-also populate the menu in code, using {@link android.view.Menu#add(int,int,int,int)
-add()} to add menu items.)</p>
+href="#Inflating">Inflating a Menu Resource</a>. For example:</p>
 
-<p>When the user selects a menu item from the Options Menu, the system calls your Activity's
+<pre>
+&#64;Override
+public boolean onCreateOptionsMenu(Menu menu) {
+    MenuInflater inflater = getMenuInflater();
+    inflater.inflate(R.menu.game_menu, menu);
+    return true;
+}
+</pre>
+
+<p>(You can also populate the menu in code, using {@link android.view.Menu#add(int,int,int,int)
+add()} to add items to the {@link android.view.Menu}.)</p>
+
+<p>When the user selects a menu item from the Options Menu (including items selected from the
+Action Bar), the system calls your Activity's
 {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}
 method. This method passes the
 {@link android.view.MenuItem} that the user selected. You can identify the menu item by calling
 {@link android.view.MenuItem#getItemId()}, which returns the unique ID for the menu
 item (defined by the {@code android:id} attribute in the menu resource or with an integer passed
 to the {@link android.view.Menu#add(int,int,int,int) add()} method). You can match this ID
-against known menu items and perform the appropriate action.</p>
-
-<p>For example:</p>
+against known menu items and perform the appropriate action. For example:</p>
 
 <pre>
 &#64;Override
@@ -200,8 +218,8 @@
     case R.id.new_game:
         newGame();
         return true;
-    case R.id.quit:
-        quit();
+    case R.id.help:
+        showHelp();
         return true;
     default:
         return super.onOptionsItemSelected(item);
@@ -224,8 +242,8 @@
 onCreateOptionsMenu()} and {@link android.app.Activity#onOptionsItemSelected(MenuItem)
 onOptionsItemSelected()} methods. Then extend this class for each Activity that should share the
 same Options Menu. This way, you have to manage only one set of code for handling menu
-actions and each decendent class inherits the menu behaviors.<br/><br/>
-If you want to add menu items to one of your decendent activities,
+actions and each descendant class inherits the menu behaviors.<br/><br/>
+If you want to add menu items to one of your descendant activities,
 override {@link android.app.Activity#onCreateOptionsMenu(Menu)
 onCreateOptionsMenu()} in that Activity. Call {@code super.onCreateOptionsMenu(menu)} so the
 original menu items are created, then add new menu items with {@link
@@ -542,7 +560,7 @@
 <h3 id="intents">Intents for menu items</h3>
 
 <p>Sometimes you'll want a menu item to launch an Activity using an Intent (whether it's an
-Actvitity in your application or another application). When you know the Intent you want to use and
+Activity in your application or another application). When you know the Intent you want to use and
 have a specific menu item that should initiate the Intent, you can execute the Intent with {@link
 android.app.Activity#startActivity(Intent) startActivity()} during the appropriate on-item-selected
 callback method (such as the {@link android.app.Activity#onOptionsItemSelected(MenuItem)
diff --git a/docs/html/images/ui/actionbar-item-withtext.png b/docs/html/images/ui/actionbar-item-withtext.png
new file mode 100644
index 0000000..98b5f84
--- /dev/null
+++ b/docs/html/images/ui/actionbar-item-withtext.png
Binary files differ
diff --git a/docs/html/images/ui/actionbar-logo.png b/docs/html/images/ui/actionbar-logo.png
new file mode 100644
index 0000000..481ed2c
--- /dev/null
+++ b/docs/html/images/ui/actionbar-logo.png
Binary files differ
diff --git a/docs/html/images/ui/actionbar.png b/docs/html/images/ui/actionbar.png
new file mode 100644
index 0000000..4fc871c
--- /dev/null
+++ b/docs/html/images/ui/actionbar.png
Binary files differ
diff --git a/docs/html/sdk/adt_download.jd b/docs/html/sdk/adt_download.jd
index 3da576a..33e480a 100644
--- a/docs/html/sdk/adt_download.jd
+++ b/docs/html/sdk/adt_download.jd
@@ -22,6 +22,13 @@
     <th>Notes</th>
   </tr>
   <tr>
+     <td>8.0.0</td>
+     <td><a href="http://dl-ssl.google.com/android/ADT-8.0.0.zip">ADT-8.0.0.zip</a></td>
+     <td><nobr> bytes</nobr></td>
+     <td></td>
+     <td>Requires SDK Tools, Revision 8 <em><nobr>November 2010</nobr></em></td>
+  </tr>
+  <tr>
      <td>0.9.9</td>
      <td><a href="http://dl-ssl.google.com/android/ADT-0.9.9.zip">ADT-0.9.9.zip</a></td>
      <td><nobr>8301681 bytes</nobr></td>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index c3afebd..8f6f518 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -99,14 +99,14 @@
 <div class="toggleable opened">
   <a href="#" onclick="return toggleDiv(this)">
         <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
-ADT 8.0</a> <em>(November 2010)</em>
+ADT 8.0.0</a> <em>(December 2010)</em>
   <div class="toggleme">
 
 <dl>
 
 <dt>Dependencies:</dt>
 
-<p><p>ADT 8.0 is designed for use with SDK Tools r8. If you haven't
+<p><p>ADT 8.0.0 is designed for use with SDK Tools r8. If you haven't
 already installed SDK Tools r8 into your SDK, use the Android SDK and AVD Manager to do
 so.</p></dd>
 
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 7ccb7a0..057d9e0 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -75,7 +75,7 @@
       </li>
     </ul>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r7</a></li>
+      <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r8</a> <span class="new">new!</span></li>
       <li><a href="<?cs var:toroot ?>sdk/win-usb.html">USB Driver for
       Windows, r3</a>
       </li>
diff --git a/docs/html/sdk/tools-notes.jd b/docs/html/sdk/tools-notes.jd
index 0f075e5..9316fae 100644
--- a/docs/html/sdk/tools-notes.jd
+++ b/docs/html/sdk/tools-notes.jd
@@ -64,6 +64,70 @@
 <div class="toggleable opened">
   <a href="#" onclick="return toggleDiv(this)">
         <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+SDK Tools, Revision 8</a> <em>(December 2010)</em>
+  <div class="toggleme">
+
+<dl>
+<dt>Dependencies:</dt>
+<dd>
+<p>If you are developing in Eclipse with ADT, note that SDK Tools r8 is
+designed for use with ADT 8.0.0 and later. After installing SDK Tools r8, we
+highly recommend updating your ADT Plugin to 8.0.0.</p>
+
+<p>Also note that SDK Tools r8 requires a new SDK component called
+<em>Platform-tools</em>. The new Platform-tools component lets all SDK platforms
+(Android 2.1, Android 2.2, and so on) use the same (latest) version of build
+tools such as <code>adb</code>, <code>aapt</code>, <code>aidl</code>, and
+<code>dx</code>. To download the Platform-tools component, use the Android SDK
+Manager, as described in <a href="adding-components.html">Adding SDK
+Components</a></p>
+
+<dt>Upgrading from SDK Tools r7:</dt>
+<dd>
+<p>If you are upgrading to SDK Tools r8 from an earlier version, note that the
+the default installed location for the <code>adb</code> tool has changed from
+<code>&lt;<em>SDK</em>&gt;/tools/adb</code> to
+<code>&lt;<em>SDK</em>&gt;/platform-tools/adb</code>. This means that you should
+add the new location to your PATH and modify any custom build scripts to
+reference the new location. Copying the <code>adb</code> executable from the new
+location to the old is not recommended, since subsequent updates to the SDK
+Tools will delete the file.</p>
+</dd>
+
+<dt>General notes:</dt>
+<dd>
+<ul>
+<li>All SDK platforms now support Library Projects.</li>
+<li>Support for a true debug build. Developers no longer need to add the
+<code>android:debuggable</code> attribute to the
+<code>&lt;application&gt;</code> tag in the manifest &mdash; the build tools add
+the attribute automatically. In Eclipse/ADT, all incremental builds are assumed
+to be debug builds, so the tools insert <code>android:debuggable="true"</code>.
+When exporting a signed release build, the tools do not add the attribute. In
+Ant, a <code>ant debug</code> command automatically inserts the
+<code>android:debuggable="true"</code> attribute, while <code>ant release</code>
+does not. If <code>android:debuggable="true"</code> is manually set, then
+<code>ant release</code> will actually do a debug build, rather than a release
+build.</li>
+<li>Automatic ProGuard support in release builds. Developers generate a ProGuard
+configuration file using the <code>android</code> tool &mdash; the build tools
+then automatically run ProGuard against the project sources during the build.
+For more information, see the <a
+href="{@docRoot}guide/developing/tools/proguard.html">ProGuard</a>
+documentation. </li>
+<li>New overridable Ant javac properties: <code>java.encoding</code>,
+<code>java.source</code>, and <code>java.target</code> (default values are
+"ascii", "1.5", and "1.5", respectively).</li>
+<li>New UI for the HierarchyViewer tool.</li>
+</ul>
+</dd>
+</dl>
+ </div>
+</div>
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)">
+        <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
 SDK Tools, Revision 7</a> <em>(September 2010)</em>
   <div class="toggleme">
 
diff --git a/drm/common/Android.mk b/drm/common/Android.mk
index 808b2c2..c79a91a 100644
--- a/drm/common/Android.mk
+++ b/drm/common/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_SRC_FILES:= \
     DrmConstraints.cpp \
+    DrmMetadata.cpp \
     DrmConvertedStatus.cpp \
     DrmEngineBase.cpp \
     DrmInfo.cpp \
diff --git a/drm/common/DrmEngineBase.cpp b/drm/common/DrmEngineBase.cpp
index 10c64ee..9b16c36 100644
--- a/drm/common/DrmEngineBase.cpp
+++ b/drm/common/DrmEngineBase.cpp
@@ -31,6 +31,10 @@
     return onGetConstraints(uniqueId, path, action);
 }
 
+DrmMetadata* DrmEngineBase::getMetadata(int uniqueId, const String8* path) {
+    return onGetMetadata(uniqueId, path);
+}
+
 status_t DrmEngineBase::initialize(int uniqueId) {
     return onInitialize(uniqueId);
 }
@@ -80,7 +84,7 @@
 }
 
 status_t DrmEngineBase::setPlaybackStatus(
-    int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+    int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
     return onSetPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
 }
 
@@ -116,7 +120,7 @@
 }
 
 status_t DrmEngineBase::openDecryptSession(
-    int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) {
+    int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length) {
     return onOpenDecryptSession(uniqueId, decryptHandle, fd, offset, length);
 }
 
@@ -146,7 +150,7 @@
 }
 
 ssize_t DrmEngineBase::pread(
-    int uniqueId, DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) {
+    int uniqueId, DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off64_t offset) {
     return onPread(uniqueId, decryptHandle, buffer, numBytes, offset);
 }
 
diff --git a/drm/common/DrmMetadata.cpp b/drm/common/DrmMetadata.cpp
new file mode 100644
index 0000000..6cc5ec1
--- /dev/null
+++ b/drm/common/DrmMetadata.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <drm/DrmMetadata.h>
+
+using namespace android;
+
+int DrmMetadata::getCount(void) const {
+	return mMetadataMap.size();
+}
+
+status_t DrmMetadata::put(const String8* key,
+                          const char* value) {
+    if((value != NULL) && (key != NULL)) {
+        int length = strlen(value);
+        char* charValue = new char[length + 1];
+
+        memcpy(charValue, value, length);
+        charValue[length] = '\0';
+        mMetadataMap.add(*key, charValue);
+    }
+    return NO_ERROR;
+}
+
+String8 DrmMetadata::get(const String8& key) const {
+    if (NULL != getValue(&key)) {
+        return String8(getValue(&key));
+    }
+    else {
+        return String8("");
+    }
+}
+
+const char* DrmMetadata::getValue(const String8* key) const {
+    if(key != NULL) {
+        if (NAME_NOT_FOUND != mMetadataMap.indexOfKey(*key)) {
+            return mMetadataMap.valueFor(*key);
+        }
+        else {
+            return NULL;
+        }
+    } else {
+        return NULL;
+    }
+}
+
+const char* DrmMetadata::getAsByteArray(const String8* key) const {
+    return getValue(key);
+}
+
+bool DrmMetadata::KeyIterator::hasNext() {
+    return mIndex < mDrmMetadata->mMetadataMap.size();
+}
+
+const String8& DrmMetadata::KeyIterator::next() {
+    const String8& key = mDrmMetadata->mMetadataMap.keyAt(mIndex);
+    mIndex++;
+    return key;
+}
+
+DrmMetadata::KeyIterator DrmMetadata::keyIterator() {
+    return KeyIterator(this);
+}
+
+DrmMetadata::KeyIterator::KeyIterator(const DrmMetadata::KeyIterator& keyIterator) :
+    mDrmMetadata(keyIterator.mDrmMetadata),
+    mIndex(keyIterator.mIndex) {
+    LOGV("DrmMetadata::KeyIterator::KeyIterator");
+}
+
+DrmMetadata::KeyIterator& DrmMetadata::KeyIterator::operator=(const DrmMetadata::KeyIterator& keyIterator) {
+    LOGV("DrmMetadata::KeyIterator::operator=");
+    mDrmMetadata = keyIterator.mDrmMetadata;
+    mIndex = keyIterator.mIndex;
+    return *this;
+}
+
+
+DrmMetadata::Iterator DrmMetadata::iterator() {
+    return Iterator(this);
+}
+
+DrmMetadata::Iterator::Iterator(const DrmMetadata::Iterator& iterator) :
+    mDrmMetadata(iterator.mDrmMetadata),
+    mIndex(iterator.mIndex) {
+    LOGV("DrmMetadata::Iterator::Iterator");
+}
+
+DrmMetadata::Iterator& DrmMetadata::Iterator::operator=(const DrmMetadata::Iterator& iterator) {
+    LOGV("DrmMetadata::Iterator::operator=");
+    mDrmMetadata = iterator.mDrmMetadata;
+    mIndex = iterator.mIndex;
+    return *this;
+}
+
+bool DrmMetadata::Iterator::hasNext() {
+    return mIndex < mDrmMetadata->mMetadataMap.size();
+}
+
+String8 DrmMetadata::Iterator::next() {
+    String8 value = String8(mDrmMetadata->mMetadataMap.editValueAt(mIndex));
+    mIndex++;
+    return value;
+}
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
index c3d12f0..75edac6 100644
--- a/drm/common/IDrmManagerService.cpp
+++ b/drm/common/IDrmManagerService.cpp
@@ -24,6 +24,7 @@
 
 #include <drm/DrmInfo.h>
 #include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
 #include <drm/DrmRights.h>
 #include <drm/DrmInfoStatus.h>
 #include <drm/DrmConvertedStatus.h>
@@ -123,6 +124,35 @@
     return drmConstraints;
 }
 
+DrmMetadata* BpDrmManagerService::getMetadata(int uniqueId, const String8* path) {
+    LOGV("Get Metadata");
+    Parcel data, reply;
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    DrmMetadata* drmMetadata = NULL;
+    data.writeString8(*path);
+    remote()->transact(GET_METADATA_FROM_CONTENT, data, &reply);
+
+    if (0 != reply.dataAvail()) {
+        //Filling Drm Metadata
+        drmMetadata = new DrmMetadata();
+
+        const int size = reply.readInt32();
+        for (int index = 0; index < size; ++index) {
+            const String8 key(reply.readString8());
+            const int bufferSize = reply.readInt32();
+            char* data = NULL;
+            if (0 < bufferSize) {
+                data = new char[bufferSize];
+                reply.read(data, bufferSize);
+            }
+            drmMetadata->put(&key, data);
+        }
+    }
+    return drmMetadata;
+}
+
 bool BpDrmManagerService::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
     LOGV("Can Handle");
     Parcel data, reply;
@@ -333,7 +363,7 @@
 }
 
 status_t BpDrmManagerService::setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
     LOGV("setPlaybackStatus");
     Parcel data, reply;
 
@@ -429,7 +459,7 @@
     if (0 != reply.dataAvail()) {
         //Filling DRM Converted Status
         const int statusCode = reply.readInt32();
-        const int offset = reply.readInt32();
+        const off64_t offset = reply.readInt64();
 
         DrmBuffer* convertedData = NULL;
         if (0 != reply.dataAvail()) {
@@ -461,7 +491,7 @@
     if (0 != reply.dataAvail()) {
         //Filling DRM Converted Status
         const int statusCode = reply.readInt32();
-        const int offset = reply.readInt32();
+        const off64_t offset = reply.readInt64();
 
         DrmBuffer* convertedData = NULL;
         if (0 != reply.dataAvail()) {
@@ -515,15 +545,15 @@
 }
 
 DecryptHandle* BpDrmManagerService::openDecryptSession(
-            int uniqueId, int fd, int offset, int length) {
+            int uniqueId, int fd, off64_t offset, off64_t length) {
     LOGV("Entering BpDrmManagerService::openDecryptSession");
     Parcel data, reply;
 
     data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
     data.writeInt32(uniqueId);
     data.writeFileDescriptor(fd);
-    data.writeInt32(offset);
-    data.writeInt32(length);
+    data.writeInt64(offset);
+    data.writeInt64(length);
 
     remote()->transact(OPEN_DECRYPT_SESSION, data, &reply);
 
@@ -697,7 +727,7 @@
 
 ssize_t BpDrmManagerService::pread(
             int uniqueId, DecryptHandle* decryptHandle, void* buffer,
-            ssize_t numBytes, off_t offset) {
+            ssize_t numBytes, off64_t offset) {
     LOGV("read");
     Parcel data, reply;
     int result;
@@ -717,7 +747,7 @@
     }
 
     data.writeInt32(numBytes);
-    data.writeInt32(offset);
+    data.writeInt64(offset);
 
     remote()->transact(PREAD, data, &reply);
     result = reply.readInt32();
@@ -825,6 +855,38 @@
         return DRM_NO_ERROR;
     }
 
+    case GET_METADATA_FROM_CONTENT:
+    {
+        LOGV("BnDrmManagerService::onTransact :GET_METADATA_FROM_CONTENT");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+        const String8 path = data.readString8();
+
+        DrmMetadata* drmMetadata = getMetadata(uniqueId, &path);
+        if (NULL != drmMetadata) {
+            //Filling DRM Metadata contents
+            reply->writeInt32(drmMetadata->getCount());
+
+            DrmMetadata::KeyIterator keyIt = drmMetadata->keyIterator();
+            while (keyIt.hasNext()) {
+                const String8 key = keyIt.next();
+                reply->writeString8(key);
+                const char* value = drmMetadata->getAsByteArray(&key);
+                int bufferSize = 0;
+                if (NULL != value) {
+                    bufferSize = strlen(value);
+                    reply->writeInt32(bufferSize + 1);
+                    reply->write(value, bufferSize + 1);
+                } else {
+                    reply->writeInt32(0);
+                }
+            }
+        }
+        delete drmMetadata; drmMetadata = NULL;
+        return NO_ERROR;
+    }
+
     case CAN_HANDLE:
     {
         LOGV("BnDrmManagerService::onTransact :CAN_HANDLE");
@@ -1121,7 +1183,7 @@
         if (NULL != drmConvertedStatus) {
             //Filling Drm Converted Ststus
             reply->writeInt32(drmConvertedStatus->statusCode);
-            reply->writeInt32(drmConvertedStatus->offset);
+            reply->writeInt64(drmConvertedStatus->offset);
 
             if (NULL != drmConvertedStatus->convertedData) {
                 const DrmBuffer* convertedData = drmConvertedStatus->convertedData;
@@ -1150,7 +1212,7 @@
         if (NULL != drmConvertedStatus) {
             //Filling Drm Converted Ststus
             reply->writeInt32(drmConvertedStatus->statusCode);
-            reply->writeInt32(drmConvertedStatus->offset);
+            reply->writeInt64(drmConvertedStatus->offset);
 
             if (NULL != drmConvertedStatus->convertedData) {
                 const DrmBuffer* convertedData = drmConvertedStatus->convertedData;
@@ -1210,7 +1272,7 @@
         const int fd = data.readFileDescriptor();
 
         DecryptHandle* handle
-            = openDecryptSession(uniqueId, fd, data.readInt32(), data.readInt32());
+            = openDecryptSession(uniqueId, fd, data.readInt64(), data.readInt64());
 
         if (NULL != handle) {
             reply->writeInt32(handle->decryptId);
@@ -1415,7 +1477,7 @@
         const int numBytes = data.readInt32();
         char* buffer = new char[numBytes];
 
-        const off_t offset = data.readInt32();
+        const off64_t offset = data.readInt64();
 
         ssize_t result = pread(uniqueId, &handle, buffer, numBytes, offset);
         reply->writeInt32(result);
diff --git a/drm/common/ReadWriteUtils.cpp b/drm/common/ReadWriteUtils.cpp
index 7ec4fa2..c16214e 100644
--- a/drm/common/ReadWriteUtils.cpp
+++ b/drm/common/ReadWriteUtils.cpp
@@ -42,7 +42,7 @@
         struct stat sb;
 
         if (fstat(fd, &sb) == 0 && sb.st_size > 0) {
-            int length = sb.st_size;
+            off64_t length = sb.st_size;
             char* bytes = new char[length];
             if (length == read(fd, (void*) bytes, length)) {
                 string.append(bytes, length);
@@ -57,7 +57,7 @@
 int ReadWriteUtils::readBytes(const String8& filePath, char** buffer) {
     FILE* file = NULL;
     file = fopen(filePath.string(), "r");
-    int length = 0;
+    off64_t length = 0;
 
     if (NULL != file) {
         int fd = fileno(file);
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index 35e62f3..9a6f787 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -23,6 +23,7 @@
 #include <drm/DrmInfoEvent.h>
 #include <drm/DrmRights.h>
 #include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
 #include <drm/DrmInfoStatus.h>
 #include <drm/DrmInfoRequest.h>
 #include <drm/DrmSupportInfo.h>
@@ -148,6 +149,15 @@
     return NULL;
 }
 
+DrmMetadata* DrmManager::getMetadata(int uniqueId, const String8* path) {
+    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        return rDrmEngine.getMetadata(uniqueId, path);
+    }
+    return NULL;
+}
+
 status_t DrmManager::installDrmEngine(int uniqueId, const String8& absolutePath) {
     mPlugInManager.loadPlugIn(absolutePath);
 
@@ -258,7 +268,7 @@
 }
 
 status_t DrmManager::setPlaybackStatus(
-    int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+    int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
     status_t result = DRM_ERROR_UNKNOWN;
     if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
         IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
@@ -370,7 +380,7 @@
     return DRM_NO_ERROR;
 }
 
-DecryptHandle* DrmManager::openDecryptSession(int uniqueId, int fd, int offset, int length) {
+DecryptHandle* DrmManager::openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length) {
     Mutex::Autolock _l(mDecryptLock);
     status_t result = DRM_ERROR_CANNOT_HANDLE;
     Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
@@ -470,7 +480,7 @@
 }
 
 ssize_t DrmManager::pread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset) {
+            void* buffer, ssize_t numBytes, off64_t offset) {
     ssize_t result = DECRYPT_FILE_ERROR;
 
     if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
index 8cf510d..0901a44 100644
--- a/drm/drmserver/DrmManagerService.cpp
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -18,18 +18,50 @@
 #define LOG_TAG "DrmManagerService(Native)"
 #include <utils/Log.h>
 
+#include <private/android_filesystem_config.h>
+
 #include <errno.h>
 #include <utils/threads.h>
 #include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
 #include <sys/stat.h>
 #include "DrmManagerService.h"
 #include "DrmManager.h"
 
 using namespace android;
 
+static Vector<uid_t> trustedUids;
+
+static bool isProtectedCallAllowed() {
+    // TODO
+    // Following implementation is just for reference.
+    // Each OEM manufacturer should implement/replace with their own solutions.
+    bool result = false;
+
+    IPCThreadState* ipcState = IPCThreadState::self();
+    uid_t uid = ipcState->getCallingUid();
+
+    for (unsigned int i = 0; i < trustedUids.size(); ++i) {
+        if (trustedUids[i] == uid) {
+            result = true;
+            break;
+        }
+    }
+    return result;
+}
+
 void DrmManagerService::instantiate() {
     LOGV("instantiate");
     defaultServiceManager()->addService(String16("drm.drmManager"), new DrmManagerService());
+
+    if (0 >= trustedUids.size()) {
+        // TODO
+        // Following implementation is just for reference.
+        // Each OEM manufacturer should implement/replace with their own solutions.
+
+        // Add trusted uids here
+        trustedUids.push(AID_MEDIA);
+    }
 }
 
 DrmManagerService::DrmManagerService() :
@@ -79,6 +111,11 @@
     return mDrmManager->getConstraints(uniqueId, path, action);
 }
 
+DrmMetadata* DrmManagerService::getMetadata(int uniqueId, const String8* path) {
+    LOGV("Entering getMetadata from content");
+    return mDrmManager->getMetadata(uniqueId, path);
+}
+
 bool DrmManagerService::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
     LOGV("Entering canHandle");
     return mDrmManager->canHandle(uniqueId, path, mimeType);
@@ -125,7 +162,7 @@
 }
 
 status_t DrmManagerService::setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
     LOGV("Entering setPlaybackStatus");
     return mDrmManager->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
 }
@@ -170,15 +207,23 @@
 }
 
 DecryptHandle* DrmManagerService::openDecryptSession(
-            int uniqueId, int fd, int offset, int length) {
+            int uniqueId, int fd, off64_t offset, off64_t length) {
     LOGV("Entering DrmManagerService::openDecryptSession");
-    return mDrmManager->openDecryptSession(uniqueId, fd, offset, length);
+    if (isProtectedCallAllowed()) {
+        return mDrmManager->openDecryptSession(uniqueId, fd, offset, length);
+    }
+
+    return NULL;
 }
 
 DecryptHandle* DrmManagerService::openDecryptSession(
             int uniqueId, const char* uri) {
     LOGV("Entering DrmManagerService::openDecryptSession with uri");
-    return mDrmManager->openDecryptSession(uniqueId, uri);
+    if (isProtectedCallAllowed()) {
+        return mDrmManager->openDecryptSession(uniqueId, uri);
+    }
+
+    return NULL;
 }
 
 status_t DrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
@@ -206,7 +251,7 @@
 }
 
 ssize_t DrmManagerService::pread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset) {
+            void* buffer, ssize_t numBytes, off64_t offset) {
     LOGV("Entering pread");
     return mDrmManager->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
 }
diff --git a/drm/java/android/drm/DrmErrorEvent.java b/drm/java/android/drm/DrmErrorEvent.java
index 9294884..20fd8aa 100644
--- a/drm/java/android/drm/DrmErrorEvent.java
+++ b/drm/java/android/drm/DrmErrorEvent.java
@@ -53,11 +53,6 @@
      * associated with all DRM schemes.
      */
     public static final int TYPE_REMOVE_ALL_RIGHTS_FAILED = 2007;
-    /**
-     * TYPE_DRM_INFO_ACQUISITION_FAILED, when failed to get the required information to
-     * communicate with the service.
-     */
-    public static final int TYPE_DRM_INFO_ACQUISITION_FAILED = 2008;
 
     /**
      * constructor to create DrmErrorEvent object with given parameters
diff --git a/drm/java/android/drm/DrmEvent.java b/drm/java/android/drm/DrmEvent.java
index 583337f..f7bc5cd 100644
--- a/drm/java/android/drm/DrmEvent.java
+++ b/drm/java/android/drm/DrmEvent.java
@@ -31,14 +31,8 @@
      * Constant field signifies that given information is processed successfully
      */
     public static final int TYPE_DRM_INFO_PROCESSED = 1002;
-    /**
-     * Constant field signifies that the required information to communicate with
-     * the service is acquired sucessfully
-     */
-    public static final int TYPE_DRM_INFO_ACQUIRED = 1003;
 
     public static final String DRM_INFO_STATUS_OBJECT = "drm_info_status_object";
-    public static final String DRM_INFO_OBJECT = "drm_info_object";
 
     private final int mUniqueId;
     private final int mType;
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 5044d36..2f54b33 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -102,8 +102,7 @@
     }
 
     private static final int ACTION_REMOVE_ALL_RIGHTS = 1001;
-    private static final int ACTION_ACQUIRE_DRM_INFO = 1002;
-    private static final int ACTION_PROCESS_DRM_INFO = 1003;
+    private static final int ACTION_PROCESS_DRM_INFO = 1002;
 
     private int mUniqueId;
     private int mNativeContext;
@@ -126,18 +125,6 @@
             HashMap<String, Object> attributes = new HashMap<String, Object>();
 
             switch(msg.what) {
-            case ACTION_ACQUIRE_DRM_INFO: {
-                final DrmInfoRequest request = (DrmInfoRequest) msg.obj;
-                DrmInfo drmInfo = _acquireDrmInfo(mUniqueId, request);
-                if (null != drmInfo) {
-                    attributes.put(DrmEvent.DRM_INFO_OBJECT, drmInfo);
-                    event = new DrmEvent(mUniqueId, DrmEvent.TYPE_DRM_INFO_ACQUIRED, null);
-                } else {
-                    error = new DrmErrorEvent(mUniqueId,
-                            DrmErrorEvent.TYPE_DRM_INFO_ACQUISITION_FAILED, null);
-                }
-                break;
-            }
             case ACTION_PROCESS_DRM_INFO: {
                 final DrmInfo drmInfo = (DrmInfo) msg.obj;
                 DrmInfoStatus status = _processDrmInfo(mUniqueId, drmInfo);
@@ -243,19 +230,14 @@
      */
     public DrmManagerClient(Context context) {
         mContext = context;
-        Looper looper;
 
-        if (null != (looper = Looper.myLooper())) {
-            mInfoHandler = new InfoHandler(looper);
-        } else if (null != (looper = Looper.getMainLooper())) {
-            mInfoHandler = new InfoHandler(looper);
-        } else {
-            mInfoHandler = null;
-        }
+        HandlerThread infoThread = new HandlerThread("DrmManagerClient.InfoHandler");
+        infoThread.start();
+        mInfoHandler = new InfoHandler(infoThread.getLooper());
 
-        HandlerThread thread = new HandlerThread("DrmManagerClient.EventHandler");
-        thread.start();
-        mEventHandler = new EventHandler(thread.getLooper());
+        HandlerThread eventThread = new HandlerThread("DrmManagerClient.EventHandler");
+        eventThread.start();
+        mEventHandler = new EventHandler(eventThread.getLooper());
 
         // save the unique id
         mUniqueId = hashCode();
@@ -335,10 +317,24 @@
         return _getConstraints(mUniqueId, path, action);
     }
 
+   /**
+    * Get metadata information from DRM content
+    *
+    * @param path Content path from where DRM metadata would be retrieved.
+    * @return ContentValues instance in which metadata key-value pairs are embedded
+    *         or null in case of failure
+    */
+    public ContentValues getMetadata(String path) {
+        if (null == path || path.equals("")) {
+            throw new IllegalArgumentException("Given path is invalid/null");
+        }
+        return _getMetadata(mUniqueId, path);
+    }
+
     /**
      * Get constraints information evaluated from DRM content
      *
-     * @param uri The Content URI of the data
+     * @param uri Content URI from where DRM constraints would be retrieved.
      * @param action Actions defined in {@link DrmStore.Action}
      * @return ContentValues instance in which constraints key-value pairs are embedded
      *         or null in case of failure
@@ -350,6 +346,20 @@
         return getConstraints(convertUriToPath(uri), action);
     }
 
+   /**
+    * Get metadata information from DRM content
+    *
+    * @param uri Content URI from where DRM metadata would be retrieved.
+    * @return ContentValues instance in which metadata key-value pairs are embedded
+    *         or null in case of failure
+    */
+    public ContentValues getMetadata(Uri uri) {
+        if (null == uri || Uri.EMPTY == uri) {
+            throw new IllegalArgumentException("Uri should be non null");
+        }
+        return getMetadata(convertUriToPath(uri));
+    }
+
     /**
      * Save DRM rights to specified rights path
      * and make association with content path.
@@ -408,7 +418,7 @@
     /**
      * Check whether the given mimetype or uri can be handled.
      *
-     * @param uri The content URI of the data
+     * @param uri Content URI of the data to be handled.
      * @param mimeType Mimetype of the object to be handled
      * @return
      *        true - if the given mimeType or path can be handled
@@ -445,20 +455,31 @@
      * Retrieves necessary information for register, unregister or rights acquisition.
      *
      * @param drmInfoRequest Request information to retrieve drmInfo
+     * @return DrmInfo Instance as a result of processing given input
+     */
+    public DrmInfo acquireDrmInfo(DrmInfoRequest drmInfoRequest) {
+        if (null == drmInfoRequest || !drmInfoRequest.isValid()) {
+            throw new IllegalArgumentException("Given drmInfoRequest is invalid/null");
+        }
+        return _acquireDrmInfo(mUniqueId, drmInfoRequest);
+    }
+
+    /**
+     * Executes given DrmInfoRequest and returns the rights information asynchronously.
+     * This is a utility API which consists of {@link #acquireDrmInfo(DrmInfoRequest)}
+     * and {@link #processDrmInfo(DrmInfo)}.
+     * It can be used if selected DRM agent can work with this combined sequences.
+     * In case of some DRM schemes, such as OMA DRM, application needs to invoke
+     * {@link #acquireDrmInfo(DrmInfoRequest)} and {@link #processDrmInfo(DrmInfo)}, separately.
+     *
+     * @param drmInfoRequest Request information to retrieve drmInfo
      * @return
      *     ERROR_NONE for success
      *     ERROR_UNKNOWN for failure
      */
-    public int acquireDrmInfo(DrmInfoRequest drmInfoRequest) {
-        if (null == drmInfoRequest || !drmInfoRequest.isValid()) {
-            throw new IllegalArgumentException("Given drmInfoRequest is invalid/null");
-        }
-        int result = ERROR_UNKNOWN;
-        if (null != mEventHandler) {
-            Message msg = mEventHandler.obtainMessage(ACTION_ACQUIRE_DRM_INFO, drmInfoRequest);
-            result = (mEventHandler.sendMessage(msg)) ? ERROR_NONE : result;
-        }
-        return result;
+    public int acquireRights(DrmInfoRequest drmInfoRequest) {
+        DrmInfo drmInfo = acquireDrmInfo(drmInfoRequest);
+        return processDrmInfo(drmInfo);
     }
 
     /**
@@ -750,6 +771,8 @@
 
     private native ContentValues _getConstraints(int uniqueId, String path, int usage);
 
+    private native ContentValues _getMetadata(int uniqueId, String path);
+
     private native boolean _canHandle(int uniqueId, String path, String mimeType);
 
     private native DrmInfoStatus _processDrmInfo(int uniqueId, DrmInfo drmInfo);
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index e5e4547..e131839 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -29,6 +29,7 @@
 #include <drm/DrmInfoRequest.h>
 #include <drm/DrmSupportInfo.h>
 #include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
 #include <drm/DrmConvertedStatus.h>
 #include <drm/drm_framework_common.h>
 
@@ -298,6 +299,43 @@
     return constraints;
 }
 
+static jobject android_drm_DrmManagerClient_getMetadataFromContent(
+            JNIEnv* env, jobject thiz, jint uniqueId, jstring jpath) {
+    LOGV("GetMetadata - Enter");
+    const String8 pathString = Utility::getStringValue(env, jpath);
+    DrmMetadata* pMetadata =
+            getDrmManagerClientImpl(env, thiz)->getMetadata(uniqueId, &pathString);
+
+    jobject metadata = NULL;
+
+    jclass localRef = NULL;
+    localRef = env->FindClass("android/content/ContentValues");
+    if (NULL != localRef && NULL != pMetadata) {
+        // Get the constructor id
+        jmethodID constructorId = NULL;
+        constructorId = env->GetMethodID(localRef, "<init>", "()V");
+        if (NULL != constructorId) {
+            // create the java DrmMetadata object
+            metadata = env->NewObject(localRef, constructorId);
+            if (NULL != metadata) {
+                DrmMetadata::KeyIterator keyIt = pMetadata->keyIterator();
+                while (keyIt.hasNext()) {
+                    String8 key = keyIt.next();
+                    // insert the entry<constraintKey, constraintValue>
+                    // to newly created java object
+                    String8 value = pMetadata->get(key);
+                    env->CallVoidMethod(metadata, env->GetMethodID(localRef, "put",
+                            "(Ljava/lang/String;Ljava/lang/String;)V"),
+                    env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+                }
+            }
+        }
+    }
+    delete pMetadata; pMetadata = NULL;
+    LOGV("GetMetadata - Exit");
+    return metadata;
+}
+
 static jobjectArray android_drm_DrmManagerClient_getAllSupportInfo(
             JNIEnv* env, jobject thiz, jint uniqueId) {
     LOGV("GetAllSupportInfo - Enter");
@@ -682,6 +720,9 @@
     {"_getConstraints", "(ILjava/lang/String;I)Landroid/content/ContentValues;",
                                     (void*)android_drm_DrmManagerClient_getConstraintsFromContent},
 
+    {"_getMetadata", "(ILjava/lang/String;)Landroid/content/ContentValues;",
+                                    (void*)android_drm_DrmManagerClient_getMetadataFromContent},
+
     {"_getAllSupportInfo", "(I)[Landroid/drm/DrmSupportInfo;",
                                     (void*)android_drm_DrmManagerClient_getAllSupportInfo},
 
diff --git a/drm/libdrmframework/DrmManagerClient.cpp b/drm/libdrmframework/DrmManagerClient.cpp
index f0439eb..578e135 100644
--- a/drm/libdrmframework/DrmManagerClient.cpp
+++ b/drm/libdrmframework/DrmManagerClient.cpp
@@ -43,6 +43,10 @@
     return mDrmManagerClientImpl->getConstraints(mUniqueId, path, action);
 }
 
+DrmMetadata* DrmManagerClient::getMetadata(const String8* path) {
+    return mDrmManagerClientImpl->getMetadata(mUniqueId, path);
+}
+
 bool DrmManagerClient::canHandle(const String8& path, const String8& mimeType) {
     return mDrmManagerClientImpl->canHandle(mUniqueId, path, mimeType);
 }
@@ -78,7 +82,7 @@
 }
 
 status_t DrmManagerClient::setPlaybackStatus(
-            DecryptHandle* decryptHandle, int playbackStatus, int position) {
+            DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
     return mDrmManagerClientImpl
             ->setPlaybackStatus(mUniqueId, decryptHandle, playbackStatus, position);
 }
@@ -112,7 +116,7 @@
     return mDrmManagerClientImpl->getAllSupportInfo(mUniqueId, length, drmSupportInfoArray);
 }
 
-DecryptHandle* DrmManagerClient::openDecryptSession(int fd, int offset, int length) {
+DecryptHandle* DrmManagerClient::openDecryptSession(int fd, off64_t offset, off64_t length) {
     return mDrmManagerClientImpl->openDecryptSession(mUniqueId, fd, offset, length);
 }
 
@@ -145,7 +149,7 @@
 }
 
 ssize_t DrmManagerClient::pread(
-            DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) {
+            DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off64_t offset) {
     Mutex::Autolock _l(mDecryptLock);
     return mDrmManagerClientImpl->pread(mUniqueId, decryptHandle, buffer, numBytes, offset);
 }
diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp
index b3ae9a7..f39131d 100644
--- a/drm/libdrmframework/DrmManagerClientImpl.cpp
+++ b/drm/libdrmframework/DrmManagerClientImpl.cpp
@@ -101,6 +101,14 @@
     return drmConstraints;
 }
 
+DrmMetadata* DrmManagerClientImpl::getMetadata(int uniqueId, const String8* path) {
+    DrmMetadata *drmMetadata = NULL;
+    if ((NULL != path) && (EMPTY_STRING != *path)) {
+        drmMetadata = getDrmManagerService()->getMetadata(uniqueId, path);
+    }
+    return drmMetadata;
+}
+
 bool DrmManagerClientImpl::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
     bool retCode = false;
     if ((EMPTY_STRING != path) || (EMPTY_STRING != mimeType)) {
@@ -170,7 +178,7 @@
 }
 
 status_t DrmManagerClientImpl::setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
     status_t status = DRM_ERROR_UNKNOWN;
     if (NULL != decryptHandle) {
         status = getDrmManagerService()->setPlaybackStatus(
@@ -231,7 +239,7 @@
 }
 
 DecryptHandle* DrmManagerClientImpl::openDecryptSession(
-            int uniqueId, int fd, int offset, int length) {
+            int uniqueId, int fd, off64_t offset, off64_t length) {
     return getDrmManagerService()->openDecryptSession(uniqueId, fd, offset, length);
 }
 
@@ -283,7 +291,7 @@
 }
 
 ssize_t DrmManagerClientImpl::pread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset) {
+            void* buffer, ssize_t numBytes, off64_t offset) {
     ssize_t retCode = INVALID_VALUE;
     if ((NULL != decryptHandle) && (NULL != buffer) && (0 < numBytes)) {
         retCode = getDrmManagerService()->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
diff --git a/drm/libdrmframework/include/DrmManager.h b/drm/libdrmframework/include/DrmManager.h
index d782f5b..e05366d 100644
--- a/drm/libdrmframework/include/DrmManager.h
+++ b/drm/libdrmframework/include/DrmManager.h
@@ -32,6 +32,7 @@
 class DrmRightsAcquisitionInfo;
 class DrmContentIds;
 class DrmConstraints;
+class DrmMetadata;
 class DrmRights;
 class DrmInfo;
 class DrmInfoStatus;
@@ -74,6 +75,8 @@
 
     DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
 
+    DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
     bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
 
     DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
@@ -92,7 +95,7 @@
     status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
 
     status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
 
     bool validateAction(
             int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -109,7 +112,7 @@
 
     status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
 
-    DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+    DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length);
 
     DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
 
@@ -124,7 +127,7 @@
     status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
 
     ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset);
+            void* buffer, ssize_t numBytes, off64_t offset);
 
     void onInfo(const DrmInfoEvent& event);
 
diff --git a/drm/libdrmframework/include/DrmManagerClientImpl.h b/drm/libdrmframework/include/DrmManagerClientImpl.h
index 1c6be46..0a7fcd1 100644
--- a/drm/libdrmframework/include/DrmManagerClientImpl.h
+++ b/drm/libdrmframework/include/DrmManagerClientImpl.h
@@ -86,6 +86,18 @@
     DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
 
     /**
+     * Get metadata information associated with input content.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return DrmMetadata
+     *         key-value pairs of metadata are embedded in it
+     * @note
+     *    In case of error, return NULL
+     */
+    DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+    /**
      * Check whether the given mimetype or path can be handled
      *
      * @param[in] uniqueId Unique identifier for a session
@@ -191,7 +203,7 @@
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
     status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
 
     /**
      * Validates whether an action on the DRM content is allowed or not.
@@ -291,7 +303,7 @@
      * @return
      *     Handle for the decryption session
      */
-    DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+    DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length);
 
     /**
      * Open the decrypt session to decrypt the given protected content
@@ -369,7 +381,7 @@
      * @return Number of bytes read. Returns -1 for Failure.
      */
     ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset);
+            void* buffer, ssize_t numBytes, off64_t offset);
 
     /**
      * Notify the event to the registered listener
diff --git a/drm/libdrmframework/include/DrmManagerService.h b/drm/libdrmframework/include/DrmManagerService.h
index 4a3aeae..d0a0db7 100644
--- a/drm/libdrmframework/include/DrmManagerService.h
+++ b/drm/libdrmframework/include/DrmManagerService.h
@@ -61,6 +61,8 @@
 
     DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
 
+    DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
     bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
 
     DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
@@ -79,7 +81,7 @@
     status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
 
     status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
 
     bool validateAction(int uniqueId, const String8& path,
             int action, const ActionDescription& description);
@@ -96,7 +98,7 @@
 
     status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
 
-    DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+    DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length);
 
     DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
 
@@ -111,7 +113,7 @@
     status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
 
     ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset);
+            void* buffer, ssize_t numBytes, off64_t offset);
 
 private:
     DrmManager* mDrmManager;
diff --git a/drm/libdrmframework/include/IDrmManagerService.h b/drm/libdrmframework/include/IDrmManagerService.h
index 1275488..2424ea5 100644
--- a/drm/libdrmframework/include/IDrmManagerService.h
+++ b/drm/libdrmframework/include/IDrmManagerService.h
@@ -27,6 +27,7 @@
 
 class DrmContentIds;
 class DrmConstraints;
+class DrmMetadata;
 class DrmRights;
 class DrmInfo;
 class DrmInfoStatus;
@@ -51,6 +52,7 @@
         SET_DRM_SERVICE_LISTENER,
         INSTALL_DRM_ENGINE,
         GET_CONSTRAINTS_FROM_CONTENT,
+        GET_METADATA_FROM_CONTENT,
         CAN_HANDLE,
         PROCESS_DRM_INFO,
         ACQUIRE_DRM_INFO,
@@ -96,6 +98,8 @@
     virtual DrmConstraints* getConstraints(
             int uniqueId, const String8* path, const int action) = 0;
 
+    virtual DrmMetadata* getMetadata(int uniqueId, const String8* path) = 0;
+
     virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType) = 0;
 
     virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0;
@@ -116,7 +120,7 @@
             int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0;
 
     virtual status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) = 0;
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) = 0;
 
     virtual bool validateAction(
             int uniqueId, const String8& path,
@@ -136,7 +140,7 @@
     virtual status_t getAllSupportInfo(
             int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) = 0;
 
-    virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length) = 0;
+    virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length) = 0;
 
     virtual DecryptHandle* openDecryptSession(int uniqueId, const char* uri) = 0;
 
@@ -152,7 +156,7 @@
             int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
 
     virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes,off_t offset) = 0;
+            void* buffer, ssize_t numBytes,off64_t offset) = 0;
 };
 
 /**
@@ -179,6 +183,8 @@
 
     virtual DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
 
+    virtual DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
     virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
 
     virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
@@ -198,7 +204,7 @@
             int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
 
     virtual status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
 
     virtual bool validateAction(
             int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -217,7 +223,7 @@
     virtual status_t getAllSupportInfo(
             int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
 
-    virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+    virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, off64_t offset, off64_t length);
 
     virtual DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
 
@@ -233,7 +239,7 @@
             int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
 
     virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset);
+            void* buffer, ssize_t numBytes, off64_t offset);
 };
 
 /**
diff --git a/drm/libdrmframework/plugins/common/include/DrmEngineBase.h b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h
index 5851af5..b61e3d3 100644
--- a/drm/libdrmframework/plugins/common/include/DrmEngineBase.h
+++ b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h
@@ -36,6 +36,8 @@
 public:
     DrmConstraints* getConstraints(int uniqueId, const String8* path, int action);
 
+    DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
     status_t initialize(int uniqueId);
 
     status_t setOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
@@ -60,7 +62,7 @@
     status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
 
     status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
 
     bool validateAction(
             int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -78,7 +80,7 @@
     DrmSupportInfo* getSupportInfo(int uniqueId);
 
     status_t openDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length);
+            int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length);
 
     status_t openDecryptSession(
             int uniqueId, DecryptHandle* decryptHandle, const char* uri);
@@ -94,7 +96,7 @@
     status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
 
     ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset);
+            void* buffer, ssize_t numBytes, off64_t offset);
 
 protected:
     /////////////////////////////////////////////////////
@@ -117,6 +119,18 @@
             int uniqueId, const String8* path, int action) = 0;
 
     /**
+     * Get metadata information associated with input content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return DrmMetadata
+     *         key-value pairs of metadata
+     * @note
+     *     In case of error, return NULL
+     */
+    virtual DrmMetadata* onGetMetadata(int uniqueId, const String8* path) = 0;
+
+    /**
      * Initialize plug-in
      *
      * @param[in] uniqueId Unique identifier for a session
@@ -254,7 +268,7 @@
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
     virtual status_t onSetPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) = 0;
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) = 0;
 
     /**
      * Validates whether an action on the DRM content is allowed or not.
@@ -355,7 +369,7 @@
      *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
      */
     virtual status_t onOpenDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) = 0;
+            int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length) = 0;
 
     /**
      * Open the decrypt session to decrypt the given protected content
@@ -436,7 +450,7 @@
      * @return Number of bytes read. Returns -1 for Failure.
      */
     virtual ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset) = 0;
+            void* buffer, ssize_t numBytes, off64_t offset) = 0;
 };
 
 };
diff --git a/drm/libdrmframework/plugins/common/include/IDrmEngine.h b/drm/libdrmframework/plugins/common/include/IDrmEngine.h
index cc03ef2..d05c24f 100644
--- a/drm/libdrmframework/plugins/common/include/IDrmEngine.h
+++ b/drm/libdrmframework/plugins/common/include/IDrmEngine.h
@@ -23,6 +23,7 @@
 
 class DrmContentIds;
 class DrmConstraints;
+class DrmMetadata;
 class DrmRights;
 class DrmInfo;
 class DrmInfoStatus;
@@ -105,6 +106,18 @@
             int uniqueId, const String8* path, int action) = 0;
 
     /**
+     * Get metadata information associated with input content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return DrmMetadata
+     *         key-value pairs of metadata
+     * @note
+     *      In case of error, return NULL
+     */
+    virtual DrmMetadata* getMetadata(int uniqueId, const String8* path) = 0;
+
+    /**
      * Get whether the given content can be handled by this plugin or not
      *
      * @param[in] uniqueId Unique identifier for a session
@@ -211,7 +224,7 @@
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
     virtual status_t setPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
-            int playbackStatus, int position) = 0;
+            int playbackStatus, int64_t position) = 0;
 
     /**
      * Validates whether an action on the DRM content is allowed or not.
@@ -312,7 +325,7 @@
      *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
      */
     virtual status_t openDecryptSession(
-        int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) = 0;
+        int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length) = 0;
 
     /**
      * Open the decrypt session to decrypt the given protected content
@@ -393,7 +406,7 @@
      * @return Number of bytes read. Returns -1 for Failure.
      */
     virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset) = 0;
+            void* buffer, ssize_t numBytes, off64_t offset) = 0;
 };
 
 };
diff --git a/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
index ddb7fd3..f941f70 100644
--- a/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
+++ b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
@@ -30,6 +30,8 @@
 protected:
     DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action);
 
+    DrmMetadata* onGetMetadata(int uniqueId, const String8* path);
+
     status_t onInitialize(int uniqueId);
 
     status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
@@ -54,7 +56,7 @@
     status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
 
     status_t onSetPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
 
     bool onValidateAction(
             int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -72,7 +74,7 @@
     DrmSupportInfo* onGetSupportInfo(int uniqueId);
 
     status_t onOpenDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length);
+            int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length);
 
     status_t onOpenDecryptSession(
             int uniqueId, DecryptHandle* decryptHandle, const char* uri);
@@ -88,7 +90,7 @@
     status_t onFinalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
 
     ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset);
+            void* buffer, ssize_t numBytes, off64_t offset);
 
 private:
     DecryptHandle* openDecryptSessionImpl();
diff --git a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
index 41f8e91..976978f 100644
--- a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
+++ b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
@@ -20,6 +20,7 @@
 
 #include <drm/DrmRights.h>
 #include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
 #include <drm/DrmInfo.h>
 #include <drm/DrmInfoEvent.h>
 #include <drm/DrmInfoStatus.h>
@@ -51,6 +52,10 @@
 
 }
 
+DrmMetadata* DrmPassthruPlugIn::onGetMetadata(int uniqueId, const String8* path) {
+    return NULL;
+}
+
 DrmConstraints* DrmPassthruPlugIn::onGetConstraints(
         int uniqueId, const String8* path, int action) {
     LOGD("DrmPassthruPlugIn::onGetConstraints From Path: %d", uniqueId);
@@ -182,7 +187,7 @@
 }
 
 status_t DrmPassthruPlugIn::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
-            int playbackStatus, int position) {
+            int playbackStatus, int64_t position) {
     LOGD("DrmPassthruPlugIn::onSetPlaybackStatus() : %d", uniqueId);
     return DRM_NO_ERROR;
 }
@@ -229,7 +234,7 @@
 }
 
 status_t DrmPassthruPlugIn::onOpenDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) {
+            int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length) {
     LOGD("DrmPassthruPlugIn::onOpenDecryptSession() : %d", uniqueId);
 
 #ifdef ENABLE_PASSTHRU_DECRYPTION
@@ -287,7 +292,7 @@
 }
 
 ssize_t DrmPassthruPlugIn::onPread(int uniqueId, DecryptHandle* decryptHandle,
-            void* buffer, ssize_t numBytes, off_t offset) {
+            void* buffer, ssize_t numBytes, off64_t offset) {
     LOGD("DrmPassthruPlugIn::onPread() : %d", uniqueId);
     return 0;
 }
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 8d5c913..241ab17 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -30,7 +30,7 @@
 
     /** Create a new compose shader, given shaders A, B, and a combining mode.
         When the mode is applied, it will be given the result from shader A as its
-        "dst", and the result of from shader B as its "src".
+        "dst", and the result from shader B as its "src".
         @param shaderA  The colors from this shader are seen as the "dst" by the mode
         @param shaderB  The colors from this shader are seen as the "src" by the mode
         @param mode     The mode that combines the colors from the two shaders. If mode
@@ -53,7 +53,7 @@
 
     /** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
         When the mode is applied, it will be given the result from shader A as its
-        "dst", and the result of from shader B as its "src".
+        "dst", and the result from shader B as its "src".
         @param shaderA  The colors from this shader are seen as the "dst" by the mode
         @param shaderB  The colors from this shader are seen as the "src" by the mode
         @param mode     The PorterDuff mode that combines the colors from the two shaders.
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 0660441..f16e045 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -685,6 +685,8 @@
         public static final int RS_MESSAGE_TO_CLIENT_ERROR = 3;
         public static final int RS_MESSAGE_TO_CLIENT_USER = 4;
 
+        public static final int RS_ERROR_FATAL_UNKNOWN = 0x1000;
+
         MessageThread(RenderScript rs) {
             super("RSMessageThread");
             mRS = rs;
@@ -722,6 +724,10 @@
                 if (msg == RS_MESSAGE_TO_CLIENT_ERROR) {
                     String e = mRS.nContextGetErrorMessage(mRS.mContext);
 
+                    if (subID >= RS_ERROR_FATAL_UNKNOWN) {
+                        throw new RSRuntimeException("Fatal error " + subID + ", details: " + e);
+                    }
+
                     if(mRS.mErrorCallback != null) {
                         mRS.mErrorCallback.mErrorMessage = e;
                         mRS.mErrorCallback.mErrorNum = subID;
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index 8d8edd6..c7f0b15 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -111,8 +111,8 @@
 };
 
 enum {
-    CAMERA_FACING_BACK = 0,
-    CAMERA_FACING_FRONT = 1 /* The camera faces to the user */
+    CAMERA_FACING_BACK = 0, /* The facing of the camera is opposite to that of the screen. */
+    CAMERA_FACING_FRONT = 1 /* The facing of the camera is the same as that of the screen. */
 };
 
 struct CameraInfo {
@@ -128,10 +128,12 @@
      * camera image needs to be rotated clockwise so it shows correctly on
      * the display in its natural orientation. It should be 0, 90, 180, or 270.
      *
-     * For example, suppose a device has a naturally tall screen, but the camera
-     * sensor is mounted in landscape. If the top side of the camera sensor is
-     * aligned with the right edge of the display in natural orientation, the
-     * value should be 90.
+     * For example, suppose a device has a naturally tall screen. The
+     * back-facing camera sensor is mounted in landscape. You are looking at
+     * the screen. If the top side of the camera sensor is aligned with the
+     * right edge of the screen in natural orientation, the value should be
+     * 90. If the top side of a front-facing camera sensor is aligned with
+     * the right of the screen, the value should be 270.
      */
     int orientation;
 };
diff --git a/include/camera/CameraHardwareInterface.h b/include/camera/CameraHardwareInterface.h
index 5465441..16f572c 100644
--- a/include/camera/CameraHardwareInterface.h
+++ b/include/camera/CameraHardwareInterface.h
@@ -269,7 +269,9 @@
      */
     virtual status_t    cancelPicture() = 0;
 
-    /** Set the camera parameters. */
+    /**
+     * Set the camera parameters. This returns BAD_VALUE if any parameter is
+     * invalid or not supported. */
     virtual status_t    setParameters(const CameraParameters& params) = 0;
 
     /** Return the camera parameters. */
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 60031a4..6364d58 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -163,16 +163,23 @@
     // the best.
     // Example value: "90". Read/write.
     static const char KEY_JPEG_QUALITY[];
-    // The orientation of the device in degrees. For example, suppose the
-    // natural position of the device is landscape. If the user takes a picture
-    // in landscape mode in 2048x1536 resolution, the rotation will be set to
-    // "0". If the user rotates the phone 90 degrees clockwise, the rotation
-    // should be set to "90".
-    // The camera driver can set orientation in the EXIF header without rotating
-    // the picture. Or the driver can rotate the picture and the EXIF thumbnail.
-    // If the Jpeg picture is rotated, the orientation in the EXIF header should
-    // be missing or 1 (row #0 is top and column #0 is left side). The driver
-    // should not set default value for this parameter.
+    // The rotation angle in degrees relative to the orientation of the camera.
+    // This affects the pictures returned from CAMERA_MSG_COMPRESSED_IMAGE. The
+    // camera driver may set orientation in the EXIF header without rotating the
+    // picture. Or the driver may rotate the picture and the EXIF thumbnail. If
+    // the Jpeg picture is rotated, the orientation in the EXIF header will be
+    // missing or 1 (row #0 is top and column #0 is left side).
+    //
+    // Note that the JPEG pictures of front-facing cameras are not mirrored
+    // as in preview display.
+    //
+    // For example, suppose the natural orientation of the device is portrait.
+    // The device is rotated 270 degrees clockwise, so the device orientation is
+    // 270. Suppose a back-facing camera sensor is mounted in landscape and the
+    // top side of the camera sensor is aligned with the right edge of the
+    // display in natural orientation. So the camera orientation is 90. The
+    // rotation should be set to 0 (270 + 90).
+    //
     // Example value: "0" or "90" or "180" or "270". Write only.
     static const char KEY_ROTATION[];
     // GPS latitude coordinate. GPSLatitude and GPSLatitudeRef will be stored in
diff --git a/include/drm/DrmManagerClient.h b/include/drm/DrmManagerClient.h
index 5963c42..e6ba3c4 100644
--- a/include/drm/DrmManagerClient.h
+++ b/include/drm/DrmManagerClient.h
@@ -25,6 +25,7 @@
 
 class DrmInfo;
 class DrmRights;
+class DrmMetadata;
 class DrmInfoEvent;
 class DrmInfoStatus;
 class DrmInfoRequest;
@@ -65,7 +66,7 @@
      * @return
      *     Handle for the decryption session
      */
-    DecryptHandle* openDecryptSession(int fd, int offset, int length);
+    DecryptHandle* openDecryptSession(int fd, off64_t offset, off64_t length);
 
     /**
      * Open the decrypt session to decrypt the given protected content
@@ -109,7 +110,7 @@
      * @return status_t
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
-    status_t setPlaybackStatus(DecryptHandle* decryptHandle, int playbackStatus, int position);
+    status_t setPlaybackStatus(DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
 
     /**
      * Initialize decryption for the given unit of the protected content
@@ -163,7 +164,7 @@
      *
      * @return Number of bytes read. Returns -1 for Failure.
      */
-    ssize_t pread(DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset);
+    ssize_t pread(DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off64_t offset);
 
     /**
      * Validates whether an action on the DRM content is allowed or not.
@@ -204,6 +205,17 @@
     DrmConstraints* getConstraints(const String8* path, const int action);
 
     /**
+     * Get metadata information associated with input content
+     *
+     * @param[in] path Path of the protected content
+     * @return DrmMetadata
+     *         key-value pairs of metadata
+     * @note
+     *     In case of error, return NULL
+     */
+    DrmMetadata* getMetadata(const String8* path);
+
+    /**
      * Check whether the given mimetype or path can be handled
      *
      * @param[in] path Path of the content needs to be handled
diff --git a/include/drm/DrmMetadata.h b/include/drm/DrmMetadata.h
new file mode 100644
index 0000000..2c7538a
--- /dev/null
+++ b/include/drm/DrmMetadata.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DRM_METADATA_H__
+#define __DRM_METADATA_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which contains the constraints information.
+ *
+ * As a result of DrmManagerClient::getMetadata(const String8*)
+ * an instance of DrmMetadata would be returned.
+ */
+class DrmMetadata {
+public:
+    /**
+     * Iterator for key
+     */
+    class KeyIterator {
+        friend class DrmMetadata;
+    private:
+        KeyIterator(DrmMetadata* drmMetadata) : mDrmMetadata(drmMetadata), mIndex(0) {}
+
+    public:
+        KeyIterator(const KeyIterator& keyIterator);
+        KeyIterator& operator=(const KeyIterator& keyIterator);
+        virtual ~KeyIterator() {}
+
+    public:
+        bool hasNext();
+        const String8& next();
+
+    private:
+        DrmMetadata* mDrmMetadata;
+        unsigned int mIndex;
+    };
+
+    /**
+     * Iterator for constraints
+     */
+    class Iterator {
+        friend class DrmMetadata;
+    private:
+        Iterator(DrmMetadata* drmMetadata) : mDrmMetadata(drmMetadata), mIndex(0) {}
+
+    public:
+        Iterator(const Iterator& iterator);
+        Iterator& operator=(const Iterator& iterator);
+        virtual ~Iterator() {}
+
+    public:
+        bool hasNext();
+        String8 next();
+
+    private:
+        DrmMetadata* mDrmMetadata;
+        unsigned int mIndex;
+    };
+
+public:
+    DrmMetadata() {}
+    virtual ~DrmMetadata() {
+        DrmMetadata::KeyIterator keyIt = this->keyIterator();
+
+        while (keyIt.hasNext()) {
+            String8 key = keyIt.next();
+            const char* value = this->getAsByteArray(&key);
+            if (NULL != value) {
+                delete[] value;
+                value = NULL;
+            }
+        }
+        mMetadataMap.clear();
+    }
+
+public:
+    int getCount(void) const;
+    status_t put(const String8* key, const char* value);
+    String8 get(const String8& key) const;
+    const char* getAsByteArray(const String8* key) const;
+    KeyIterator keyIterator();
+    Iterator iterator();
+
+private:
+    const char* getValue(const String8* key) const;
+
+private:
+    typedef KeyedVector<String8, const char*> DrmMetadataMap;
+    DrmMetadataMap mMetadataMap;
+};
+
+};
+
+#endif /* __DRM_METADATA_H__ */
+
diff --git a/include/media/stagefright/ColorConverter.h b/include/media/stagefright/ColorConverter.h
index bc3f464..2b61f58 100644
--- a/include/media/stagefright/ColorConverter.h
+++ b/include/media/stagefright/ColorConverter.h
@@ -33,35 +33,47 @@
     bool isValid() const;
 
     void convert(
-            size_t width, size_t height,
-            const void *srcBits, size_t srcSkip,
-            void *dstBits, size_t dstSkip);
+            const void *srcBits,
+            size_t srcWidth, size_t srcHeight,
+            size_t srcCropLeft, size_t srcCropTop,
+            size_t srcCropRight, size_t srcCropBottom,
+            void *dstBits,
+            size_t dstWidth, size_t dstHeight,
+            size_t dstCropLeft, size_t dstCropTop,
+            size_t dstCropRight, size_t dstCropBottom);
 
 private:
+    struct BitmapParams {
+        BitmapParams(
+                void *bits,
+                size_t width, size_t height,
+                size_t cropLeft, size_t cropTop,
+                size_t cropRight, size_t cropBottom);
+
+        size_t cropWidth() const;
+        size_t cropHeight() const;
+
+        void *mBits;
+        size_t mWidth, mHeight;
+        size_t mCropLeft, mCropTop, mCropRight, mCropBottom;
+    };
+
     OMX_COLOR_FORMATTYPE mSrcFormat, mDstFormat;
     uint8_t *mClip;
 
     uint8_t *initClip();
 
     void convertCbYCrY(
-            size_t width, size_t height,
-            const void *srcBits, size_t srcSkip,
-            void *dstBits, size_t dstSkip);
+            const BitmapParams &src, const BitmapParams &dst);
 
     void convertYUV420Planar(
-            size_t width, size_t height,
-            const void *srcBits, size_t srcSkip,
-            void *dstBits, size_t dstSkip);
+            const BitmapParams &src, const BitmapParams &dst);
 
     void convertQCOMYUV420SemiPlanar(
-            size_t width, size_t height,
-            const void *srcBits, size_t srcSkip,
-            void *dstBits, size_t dstSkip);
+            const BitmapParams &src, const BitmapParams &dst);
 
     void convertYUV420SemiPlanar(
-            size_t width, size_t height,
-            const void *srcBits, size_t srcSkip,
-            void *dstBits, size_t dstSkip);
+            const BitmapParams &src, const BitmapParams &dst);
 
     ColorConverter(const ColorConverter &);
     ColorConverter &operator=(const ColorConverter &);
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 295b127..5f33739 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -32,6 +32,10 @@
     kKeyMIMEType          = 'mime',  // cstring
     kKeyWidth             = 'widt',  // int32_t
     kKeyHeight            = 'heig',  // int32_t
+
+    // a rectangle, if absent assumed to be (0, 0, width - 1, height - 1)
+    kKeyCropRect          = 'crop',
+
     kKeyRotation          = 'rotA',  // int32_t (angle in degrees)
     kKeyIFramesInterval   = 'ifiv',  // int32_t
     kKeyStride            = 'strd',  // int32_t
@@ -125,6 +129,7 @@
         TYPE_INT64    = 'in64',
         TYPE_FLOAT    = 'floa',
         TYPE_POINTER  = 'ptr ',
+        TYPE_RECT     = 'rect',
     };
 
     void clear();
@@ -136,12 +141,22 @@
     bool setFloat(uint32_t key, float value);
     bool setPointer(uint32_t key, void *value);
 
+    bool setRect(
+            uint32_t key,
+            int32_t left, int32_t top,
+            int32_t right, int32_t bottom);
+
     bool findCString(uint32_t key, const char **value);
     bool findInt32(uint32_t key, int32_t *value);
     bool findInt64(uint32_t key, int64_t *value);
     bool findFloat(uint32_t key, float *value);
     bool findPointer(uint32_t key, void **value);
 
+    bool findRect(
+            uint32_t key,
+            int32_t *left, int32_t *top,
+            int32_t *right, int32_t *bottom);
+
     bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
 
     bool findData(uint32_t key, uint32_t *type,
@@ -187,6 +202,10 @@
         }
     };
 
+    struct Rect {
+        int32_t mLeft, mTop, mRight, mBottom;
+    };
+
     KeyedVector<uint32_t, typed_data> mItems;
 
     // MetaData &operator=(const MetaData &);
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 5ebd2c0..00de39b 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -111,11 +111,6 @@
         resource->setPixels(NULL, NULL);
         return;
     }
-    recycle((void*) resource);
-}
-
-void ResourceCache::recycle(void* resource) {
-    Mutex::Autolock _l(mLock);
     ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
     if (ref == NULL) {
         // Should not get here - shouldn't get a call to recycle if we're not yet tracking it
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index b0abe2c..1bb4390 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -61,7 +61,6 @@
     void decrementRefcount(SkBitmap* resource);
     void decrementRefcount(SkiaShader* resource);
     void decrementRefcount(SkiaColorFilter* resource);
-    void recycle(void* resource);
     void recycle(SkBitmap* resource);
     void destructor(SkBitmap* resource);
     void destructor(SkiaShader* resource);
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 0ed129f..f00f748 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -236,11 +236,16 @@
 };
 
 enum RsError {
-    RS_ERROR_NONE,
-    RS_ERROR_BAD_SHADER,
-    RS_ERROR_BAD_SCRIPT,
-    RS_ERROR_BAD_VALUE,
-    RS_ERROR_OUT_OF_MEMORY
+    RS_ERROR_NONE = 0,
+    RS_ERROR_BAD_SHADER = 1,
+    RS_ERROR_BAD_SCRIPT = 2,
+    RS_ERROR_BAD_VALUE = 3,
+    RS_ERROR_OUT_OF_MEMORY = 4,
+    RS_ERROR_DRIVER = 5,
+
+    RS_ERROR_FATAL_UNKNOWN = 0x1000,
+    RS_ERROR_FATAL_DRIVER = 0x1001,
+    RS_ERROR_FATAL_PROGRAM_LINK = 0x1002
 };
 
 enum RsAnimationInterpolation {
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 1b584c8..7b35305 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -43,11 +43,6 @@
 	param int32_t bits
 }
 
-ContextGetError {
-	param RsError *err
-	ret const char *
-	}
-
 ContextSetPriority {
 	param int32_t priority
 	}
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 840a10e..35db332 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -287,10 +287,27 @@
     return ret;
 }
 
-void Context::checkError(const char *msg) const {
+void Context::checkError(const char *msg, bool isFatal) const {
+
     GLenum err = glGetError();
     if (err != GL_NO_ERROR) {
-        LOGE("%p, GL Error, 0x%x, from %s", this, err, msg);
+        char buf[1024];
+        snprintf(buf, sizeof(buf), "GL Error = 0x%08x, from: %s", err, msg);
+
+        if (isFatal) {
+            setError(RS_ERROR_FATAL_DRIVER, buf);
+        } else {
+            switch (err) {
+            case GL_OUT_OF_MEMORY:
+                setError(RS_ERROR_OUT_OF_MEMORY, buf);
+                break;
+            default:
+                setError(RS_ERROR_DRIVER, buf);
+                break;
+            }
+        }
+
+        LOGE("%p, %s", this, buf);
     }
 }
 
@@ -597,7 +614,6 @@
     mPaused = false;
     mObjHead = NULL;
     mError = RS_ERROR_NONE;
-    mErrorMsg = NULL;
 }
 
 Context * Context::createContext(Device *dev, const RsSurfaceConfig *sc) {
@@ -861,7 +877,8 @@
     return RS_MESSAGE_TO_CLIENT_RESIZE;
 }
 
-bool Context::sendMessageToClient(const void *data, RsMessageToClientType cmdID, uint32_t subID, size_t len, bool waitForSpace) {
+bool Context::sendMessageToClient(const void *data, RsMessageToClientType cmdID,
+                                  uint32_t subID, size_t len, bool waitForSpace) const {
     //LOGE("sendMessageToClient %i %i %i %i", cmdID, subID, len, waitForSpace);
     if (cmdID == 0) {
         LOGE("Attempting to send invalid command 0 to client.");
@@ -894,18 +911,8 @@
     mIO.mToClient.shutdown();
 }
 
-const char * Context::getError(RsError *err) {
-    *err = mError;
-    mError = RS_ERROR_NONE;
-    if (*err != RS_ERROR_NONE) {
-        return mErrorMsg;
-    }
-    return NULL;
-}
-
-void Context::setError(RsError e, const char *msg) {
+void Context::setError(RsError e, const char *msg) const {
     mError = e;
-    mErrorMsg = msg;
     sendMessageToClient(msg, RS_MESSAGE_TO_CLIENT_ERROR, e, strlen(msg) + 1, true);
 }
 
@@ -1012,14 +1019,6 @@
     ObjectBase::dumpAll(rsc);
 }
 
-const char* rsi_ContextGetError(Context *rsc, RsError *e) {
-    const char *msg = rsc->getError(e);
-    if (*e != RS_ERROR_NONE) {
-        LOGE("RS Error %i %s", *e, msg);
-    }
-    return msg;
-}
-
 }
 }
 
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index c377c73..cafbdff5 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -144,7 +144,7 @@
 
     RsMessageToClientType peekMessageToClient(size_t *receiveLen, uint32_t *subID, bool wait);
     RsMessageToClientType getMessageToClient(void *data, size_t *receiveLen, uint32_t *subID, size_t bufferLen, bool wait);
-    bool sendMessageToClient(const void *data, RsMessageToClientType cmdID, uint32_t subID, size_t len, bool waitForSpace);
+    bool sendMessageToClient(const void *data, RsMessageToClientType cmdID, uint32_t subID, size_t len, bool waitForSpace) const;
     uint32_t runScript(Script *s);
 
     void initToClient();
@@ -169,7 +169,7 @@
     uint32_t getWidth() const {return mWidth;}
     uint32_t getHeight() const {return mHeight;}
 
-    ThreadIO mIO;
+    mutable ThreadIO mIO;
 
     // Timers
     enum Timers {
@@ -197,9 +197,8 @@
     } props;
 
     void dumpDebug() const;
-    void checkError(const char *) const;
-    const char * getError(RsError *);
-    void setError(RsError e, const char *msg = NULL);
+    void checkError(const char *, bool isFatal = false) const;
+    void setError(RsError e, const char *msg = NULL) const;
 
     mutable const ObjectBase * mObjHead;
 
@@ -259,8 +258,7 @@
     bool mRunning;
     bool mExit;
     bool mPaused;
-    RsError mError;
-    const char *mErrorMsg;
+    mutable RsError mError;
 
     pthread_t mThreadId;
     pid_t mNativeThreadId;
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 40321fe..6ae8bb8 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -348,7 +348,6 @@
                             RsDataKind dk,
                             bool norm,
                             uint32_t vecSize) {
-    //LOGE("rsi_ElementCreate %i %i %i %i", dt, dk, norm, vecSize);
     const Element *e = Element::create(rsc, dt, dk, norm, vecSize);
     e->incUserRef();
     return (RsElement)e;
@@ -360,7 +359,6 @@
                              const char ** names,
                              const size_t * nameLengths,
                              const uint32_t * arraySizes) {
-    //LOGE("rsi_ElementCreate2 %i", count);
     const Element *e = Element::create(rsc, count, (const Element **)ein, names, nameLengths, arraySizes);
     e->incUserRef();
     return (RsElement)e;
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index ffa7d26..0713fb3 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -136,9 +136,9 @@
         char buf[256];
         for (uint32_t ct=0; ct < mTextureCount; ct++) {
             if (mTextureTargets[ct] == RS_TEXTURE_2D) {
-                sprintf(buf, "uniform sampler2D UNI_Tex%i;\n", ct);
+                snprintf(buf, sizeof(buf), "uniform sampler2D UNI_Tex%i;\n", ct);
             } else {
-                sprintf(buf, "uniform samplerCube UNI_Tex%i;\n", ct);
+                snprintf(buf, sizeof(buf), "uniform samplerCube UNI_Tex%i;\n", ct);
             }
             mShader.append(buf);
         }
@@ -159,7 +159,7 @@
     mTextureUniformIndexStart = uniformIndex;
     char buf[256];
     for (uint32_t ct=0; ct < mTextureCount; ct++) {
-        sprintf(buf, "UNI_Tex%i", ct);
+        snprintf(buf, sizeof(buf), "UNI_Tex%i", ct);
         mUniformNames[uniformIndex].setTo(buf);
         mUniformArraySizes[uniformIndex] = 1;
         uniformIndex++;
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 3fd2981..a28b9bd 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -49,7 +49,7 @@
     Program::loadShader(rsc, GL_VERTEX_SHADER);
 }
 
-void ProgramVertex::createShader() {
+void ProgramVertex::createShader(Context *rsc) {
     if (mUserShader.length() > 1) {
 
         appendUserConstants();
@@ -81,13 +81,12 @@
         }
         mShader.append(mUserShader);
     } else {
-        LOGE("ProgramFragment::createShader cannot create program, shader code not defined");
-        rsAssert(0);
+        rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+                      "ProgramFragment::createShader cannot create program, shader code not defined");
     }
 }
 
 void ProgramVertex::setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc) {
-    //LOGE("sgl2 vtx1 %x", glGetError());
     if ((state->mLast.get() == this) && !mDirty) {
         return;
     }
@@ -96,8 +95,8 @@
 
     if (!isUserProgram()) {
         if (mConstants[0].get() == NULL) {
-            LOGE("Unable to set fixed function emulation matrices because allocation is missing");
-            rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+            rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+                          "Unable to set fixed function emulation matrices because allocation is missing");
             return;
         }
         float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -120,12 +119,13 @@
 
 void ProgramVertex::setProjectionMatrix(Context *rsc, const rsc_Matrix *m) const {
     if (isUserProgram()) {
-        LOGE("Attempting to set fixed function emulation matrix projection on user program");
-        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+        rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+                      "Attempting to set fixed function emulation matrix projection on user program");
         return;
     }
     if (mConstants[0].get() == NULL) {
-        LOGE("Unable to set fixed function emulation matrix projection because allocation is missing");
+        rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+                      "Unable to set fixed function emulation matrix projection because allocation is missing");
         return;
     }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -135,13 +135,13 @@
 
 void ProgramVertex::setModelviewMatrix(Context *rsc, const rsc_Matrix *m) const {
     if (isUserProgram()) {
-        LOGE("Attempting to set fixed function emulation matrix modelview on user program");
-        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+        rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+                      "Attempting to set fixed function emulation matrix modelview on user program");
         return;
     }
     if (mConstants[0].get() == NULL) {
-        LOGE("Unable to set fixed function emulation matrix modelview because allocation is missing");
-        rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+        rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+                      "Unable to set fixed function emulation matrix modelview because allocation is missing");
         return;
     }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -151,13 +151,13 @@
 
 void ProgramVertex::setTextureMatrix(Context *rsc, const rsc_Matrix *m) const {
     if (isUserProgram()) {
-        LOGE("Attempting to set fixed function emulation matrix texture on user program");
-        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+        rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+                      "Attempting to set fixed function emulation matrix texture on user program");
         return;
     }
     if (mConstants[0].get() == NULL) {
-        LOGE("Unable to set fixed function emulation matrix texture because allocation is missing");
-        rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+        rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+                      "Unable to set fixed function emulation matrix texture because allocation is missing");
         return;
     }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -167,13 +167,13 @@
 
 void ProgramVertex::getProjectionMatrix(Context *rsc, rsc_Matrix *m) const {
     if (isUserProgram()) {
-        LOGE("Attempting to get fixed function emulation matrix projection on user program");
-        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot get emulation matrix on user shader");
+        rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+                      "Attempting to get fixed function emulation matrix projection on user program");
         return;
     }
     if (mConstants[0].get() == NULL) {
-        LOGE("Unable to get fixed function emulation matrix projection because allocation is missing");
-        rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+        rsc->setError(RS_ERROR_FATAL_UNKNOWN,
+                      "Unable to get fixed function emulation matrix projection because allocation is missing");
         return;
     }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
@@ -202,7 +202,7 @@
             initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI);
         }
     }
-    createShader();
+    createShader(rsc);
 }
 
 void ProgramVertex::serialize(OStream *stream) const {
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index 824edbb..2a5c863 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -40,7 +40,7 @@
 
     void transformToScreen(Context *, float *v4out, const float *v3in) const;
 
-    virtual void createShader();
+    virtual void createShader(Context *);
     virtual void loadShader(Context *);
     virtual void init(Context *);
 
diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp
index d254018..45384c9 100644
--- a/libs/rs/rsShaderCache.cpp
+++ b/libs/rs/rsShaderCache.cpp
@@ -150,7 +150,7 @@
                 }
             }
             glDeleteProgram(pgm);
-            rsc->setError(RS_ERROR_BAD_SHADER, "Error linking GL Programs");
+            rsc->setError(RS_ERROR_FATAL_PROGRAM_LINK, "Error linking GL Programs");
             return false;
         }
 
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
index 2a51335..c195b9b 100644
--- a/libs/rs/rsType.cpp
+++ b/libs/rs/rsType.cpp
@@ -136,7 +136,7 @@
     char buf[1024];
     ObjectBase::dumpLOGV(prefix);
     LOGV("%s   Type: x=%i y=%i z=%i mip=%i face=%i", prefix, mDimX, mDimY, mDimZ, mDimLOD, mFaces);
-    sprintf(buf, "%s element: ", prefix);
+    snprintf(buf, sizeof(buf), "%s element: ", prefix);
     mElement->dumpLOGV(buf);
 }
 
diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp
index 3aa8950..b082c53 100644
--- a/libs/ui/Overlay.cpp
+++ b/libs/ui/Overlay.cpp
@@ -96,7 +96,6 @@
 }
 
 void Overlay::destroy() {  
-    if (mStatus != NO_ERROR) return;
 
     // Must delete the objects in reverse creation order, thus the
     //  data side must be closed first and then the destroy send to
@@ -104,9 +103,15 @@
     if (mOverlayData) {
         overlay_data_close(mOverlayData);
         mOverlayData = NULL;
+    } else {
+        LOGD("Overlay::destroy mOverlayData is NULL");
     }
 
-    mOverlayRef->mOverlayChannel->destroy();
+    if (mOverlayRef != 0) {
+        mOverlayRef->mOverlayChannel->destroy();
+    } else {
+        LOGD("Overlay::destroy mOverlayRef is NULL");
+    }
 }
 
 status_t Overlay::getStatus() const {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index def88ae..9058a7b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -984,7 +984,8 @@
      * application when it places a phone call, as it will cause signals from the radio layer
      * to feed the platform mixer.
      *
-     * @param mode  the requested audio mode (NORMAL, RINGTONE, IN_CALL or IN_COMMUNICATION).
+     * @param mode  the requested audio mode ({@link #MODE_NORMAL}, {@link #MODE_RINGTONE},
+     *              {@link #MODE_IN_CALL} or {@link #MODE_IN_COMMUNICATION}).
      *              Informs the HAL about the current audio state so that
      *              it can route the audio appropriately.
      */
@@ -1000,7 +1001,8 @@
     /**
      * Returns the current audio mode.
      *
-     * @return      the current audio mode (NORMAL, RINGTONE, IN_CALL or IN_COMMUNICATION).
+     * @return      the current audio mode ({@link #MODE_NORMAL}, {@link #MODE_RINGTONE},
+     *              {@link #MODE_IN_CALL} or {@link #MODE_IN_COMMUNICATION}).
      *              Returns the current current audio state from the HAL.
      */
     public int getMode() {
@@ -1038,7 +1040,6 @@
      */
     public static final int MODE_IN_CALL            = AudioSystem.MODE_IN_CALL;
     /**
-     * @hide
      * In communication audio mode. An audio/video chat or VoIP call is established.
      */
     public static final int MODE_IN_COMMUNICATION   = AudioSystem.MODE_IN_COMMUNICATION;
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index e48e9e8..250ec44 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -25,11 +25,11 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.RemoteException;
+import android.provider.MediaStore;
 import android.provider.MediaStore.Audio;
 import android.provider.MediaStore.Files;
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.MediaColumns;
-import android.provider.Mtp;
 import android.util.Log;
 
 import java.io.File;
@@ -1023,7 +1023,7 @@
         Log.d(TAG, "sessionEnded");
         if (mDatabaseModified) {
             Log.d(TAG, "sending ACTION_MTP_SESSION_END");
-            mContext.sendBroadcast(new Intent(Mtp.ACTION_MTP_SESSION_END));
+            mContext.sendBroadcast(new Intent(MediaStore.ACTION_MTP_SESSION_END));
             mDatabaseModified = false;
         }
     }
diff --git a/media/java/android/media/MtpClient.java b/media/java/android/media/PtpClient.java
similarity index 90%
rename from media/java/android/media/MtpClient.java
rename to media/java/android/media/PtpClient.java
index dcb1983..2e40635 100644
--- a/media/java/android/media/MtpClient.java
+++ b/media/java/android/media/PtpClient.java
@@ -21,9 +21,9 @@
 /**
  * {@hide}
  */
-public class MtpClient {
+public class PtpClient {
 
-    private static final String TAG = "MtpClient";
+    private static final String TAG = "PtpClient";
 
     private final Listener mListener;
 
@@ -31,10 +31,10 @@
         System.loadLibrary("media_jni");
     }
 
-    public MtpClient(Listener listener) {
+    public PtpClient(Listener listener) {
         native_setup();
         if (listener == null) {
-            throw new NullPointerException("MtpClient: listener is null");
+            throw new NullPointerException("PtpClient: listener is null");
         }
         mListener = listener;
     }
@@ -75,10 +75,10 @@
     }
 
     public interface Listener {
-        // called when a new MTP device has been discovered
+        // called when a new PTP device has been discovered
         void deviceAdded(int id);
 
-        // called when an MTP device has been removed
+        // called when an PTP device has been removed
         void deviceRemoved(int id);
     }
 
diff --git a/media/java/android/media/MtpCursor.java b/media/java/android/media/PtpCursor.java
similarity index 75%
rename from media/java/android/media/MtpCursor.java
rename to media/java/android/media/PtpCursor.java
index daa3f4d..bb5b1ec 100644
--- a/media/java/android/media/MtpCursor.java
+++ b/media/java/android/media/PtpCursor.java
@@ -18,17 +18,17 @@
 
 import android.database.AbstractWindowedCursor;
 import android.database.CursorWindow;
-import android.provider.Mtp;
+import android.provider.Ptp;
 import android.util.Log;
 
 import java.util.HashMap;
 
 /**
-  * Cursor class for MTP content provider
+  * Cursor class for PTP content provider
   * @hide
   */
-public final class MtpCursor extends AbstractWindowedCursor {
-    static final String TAG = "MtpCursor";
+public final class PtpCursor extends AbstractWindowedCursor {
+    static final String TAG = "PtpCursor";
     static final int NO_COUNT = -1;
 
     /* constants for queryType */
@@ -49,10 +49,10 @@
     private int mCount = NO_COUNT;
 
 
-    public MtpCursor(MtpClient client, int queryType, int deviceID, long storageID, long objectID,
+    public PtpCursor(PtpClient client, int queryType, int deviceID, long storageID, long objectID,
             String[] projection) {
         if (client == null) {
-            throw new NullPointerException("client null in MtpCursor constructor");
+            throw new NullPointerException("client null in PtpCursor constructor");
         }
         mColumns = projection;
 
@@ -132,19 +132,19 @@
     }
 
     /* Device Column IDs */
-     /* These must match the values in MtpCursor.cpp */
+     /* These must match the values in PtpCursor.cpp */
    private static final int DEVICE_ROW_ID          = 1;
     private static final int DEVICE_MANUFACTURER    = 2;
     private static final int DEVICE_MODEL           = 3;
 
     /* Storage Column IDs */
-    /* These must match the values in MtpCursor.cpp */
+    /* These must match the values in PtpCursor.cpp */
     private static final int STORAGE_ROW_ID         = 101;
     private static final int STORAGE_IDENTIFIER     = 102;
     private static final int STORAGE_DESCRIPTION    = 103;
 
     /* Object Column IDs */
-    /* These must match the values in MtpCursor.cpp */
+    /* These must match the values in PtpCursor.cpp */
     private static final int OBJECT_ROW_ID              = 201;
     private static final int OBJECT_STORAGE_ID          = 202;
     private static final int OBJECT_FORMAT              = 203;
@@ -173,45 +173,45 @@
 
     static {
         sDeviceProjectionMap = new HashMap<String, Integer>();
-        sDeviceProjectionMap.put(Mtp.Device._ID, new Integer(DEVICE_ROW_ID));
-        sDeviceProjectionMap.put(Mtp.Device.MANUFACTURER, new Integer(DEVICE_MANUFACTURER));
-        sDeviceProjectionMap.put(Mtp.Device.MODEL, new Integer(DEVICE_MODEL));
+        sDeviceProjectionMap.put(Ptp.Device._ID, new Integer(DEVICE_ROW_ID));
+        sDeviceProjectionMap.put(Ptp.Device.MANUFACTURER, new Integer(DEVICE_MANUFACTURER));
+        sDeviceProjectionMap.put(Ptp.Device.MODEL, new Integer(DEVICE_MODEL));
 
         sStorageProjectionMap = new HashMap<String, Integer>();
-        sStorageProjectionMap.put(Mtp.Storage._ID, new Integer(STORAGE_ROW_ID));
-        sStorageProjectionMap.put(Mtp.Storage.IDENTIFIER, new Integer(STORAGE_IDENTIFIER));
-        sStorageProjectionMap.put(Mtp.Storage.DESCRIPTION, new Integer(STORAGE_DESCRIPTION));
+        sStorageProjectionMap.put(Ptp.Storage._ID, new Integer(STORAGE_ROW_ID));
+        sStorageProjectionMap.put(Ptp.Storage.IDENTIFIER, new Integer(STORAGE_IDENTIFIER));
+        sStorageProjectionMap.put(Ptp.Storage.DESCRIPTION, new Integer(STORAGE_DESCRIPTION));
 
         sObjectProjectionMap = new HashMap<String, Integer>();
-        sObjectProjectionMap.put(Mtp.Object._ID, new Integer(OBJECT_ROW_ID));
-        sObjectProjectionMap.put(Mtp.Object.STORAGE_ID, new Integer(OBJECT_STORAGE_ID));
-        sObjectProjectionMap.put(Mtp.Object.FORMAT, new Integer(OBJECT_FORMAT));
-        sObjectProjectionMap.put(Mtp.Object.PROTECTION_STATUS, new Integer(OBJECT_PROTECTION_STATUS));
-        sObjectProjectionMap.put(Mtp.Object.SIZE, new Integer(OBJECT_SIZE));
-        sObjectProjectionMap.put(Mtp.Object.THUMB_FORMAT, new Integer(OBJECT_THUMB_FORMAT));
-        sObjectProjectionMap.put(Mtp.Object.THUMB_SIZE, new Integer(OBJECT_THUMB_SIZE));
-        sObjectProjectionMap.put(Mtp.Object.THUMB_WIDTH, new Integer(OBJECT_THUMB_WIDTH));
-        sObjectProjectionMap.put(Mtp.Object.THUMB_HEIGHT, new Integer(OBJECT_THUMB_HEIGHT));
-        sObjectProjectionMap.put(Mtp.Object.IMAGE_WIDTH, new Integer(OBJECT_IMAGE_WIDTH));
-        sObjectProjectionMap.put(Mtp.Object.IMAGE_HEIGHT, new Integer(OBJECT_IMAGE_HEIGHT));
-        sObjectProjectionMap.put(Mtp.Object.IMAGE_DEPTH, new Integer(OBJECT_IMAGE_DEPTH));
-        sObjectProjectionMap.put(Mtp.Object.PARENT, new Integer(OBJECT_PARENT));
-        sObjectProjectionMap.put(Mtp.Object.ASSOCIATION_TYPE, new Integer(OBJECT_ASSOCIATION_TYPE));
-        sObjectProjectionMap.put(Mtp.Object.ASSOCIATION_DESC, new Integer(OBJECT_ASSOCIATION_DESC));
-        sObjectProjectionMap.put(Mtp.Object.SEQUENCE_NUMBER, new Integer(OBJECT_SEQUENCE_NUMBER));
-        sObjectProjectionMap.put(Mtp.Object.NAME, new Integer(OBJECT_NAME));
-        sObjectProjectionMap.put(Mtp.Object.DATE_CREATED, new Integer(OBJECT_DATE_CREATED));
-        sObjectProjectionMap.put(Mtp.Object.DATE_MODIFIED, new Integer(OBJECT_DATE_MODIFIED));
-        sObjectProjectionMap.put(Mtp.Object.KEYWORDS, new Integer(OBJECT_KEYWORDS));
-        sObjectProjectionMap.put(Mtp.Object.THUMB, new Integer(OBJECT_THUMB));
+        sObjectProjectionMap.put(Ptp.Object._ID, new Integer(OBJECT_ROW_ID));
+        sObjectProjectionMap.put(Ptp.Object.STORAGE_ID, new Integer(OBJECT_STORAGE_ID));
+        sObjectProjectionMap.put(Ptp.Object.FORMAT, new Integer(OBJECT_FORMAT));
+        sObjectProjectionMap.put(Ptp.Object.PROTECTION_STATUS, new Integer(OBJECT_PROTECTION_STATUS));
+        sObjectProjectionMap.put(Ptp.Object.SIZE, new Integer(OBJECT_SIZE));
+        sObjectProjectionMap.put(Ptp.Object.THUMB_FORMAT, new Integer(OBJECT_THUMB_FORMAT));
+        sObjectProjectionMap.put(Ptp.Object.THUMB_SIZE, new Integer(OBJECT_THUMB_SIZE));
+        sObjectProjectionMap.put(Ptp.Object.THUMB_WIDTH, new Integer(OBJECT_THUMB_WIDTH));
+        sObjectProjectionMap.put(Ptp.Object.THUMB_HEIGHT, new Integer(OBJECT_THUMB_HEIGHT));
+        sObjectProjectionMap.put(Ptp.Object.IMAGE_WIDTH, new Integer(OBJECT_IMAGE_WIDTH));
+        sObjectProjectionMap.put(Ptp.Object.IMAGE_HEIGHT, new Integer(OBJECT_IMAGE_HEIGHT));
+        sObjectProjectionMap.put(Ptp.Object.IMAGE_DEPTH, new Integer(OBJECT_IMAGE_DEPTH));
+        sObjectProjectionMap.put(Ptp.Object.PARENT, new Integer(OBJECT_PARENT));
+        sObjectProjectionMap.put(Ptp.Object.ASSOCIATION_TYPE, new Integer(OBJECT_ASSOCIATION_TYPE));
+        sObjectProjectionMap.put(Ptp.Object.ASSOCIATION_DESC, new Integer(OBJECT_ASSOCIATION_DESC));
+        sObjectProjectionMap.put(Ptp.Object.SEQUENCE_NUMBER, new Integer(OBJECT_SEQUENCE_NUMBER));
+        sObjectProjectionMap.put(Ptp.Object.NAME, new Integer(OBJECT_NAME));
+        sObjectProjectionMap.put(Ptp.Object.DATE_CREATED, new Integer(OBJECT_DATE_CREATED));
+        sObjectProjectionMap.put(Ptp.Object.DATE_MODIFIED, new Integer(OBJECT_DATE_MODIFIED));
+        sObjectProjectionMap.put(Ptp.Object.KEYWORDS, new Integer(OBJECT_KEYWORDS));
+        sObjectProjectionMap.put(Ptp.Object.THUMB, new Integer(OBJECT_THUMB));
 
-        sObjectProjectionMap.put(Mtp.Object.NAME, new Integer(OBJECT_NAME));
+        sObjectProjectionMap.put(Ptp.Object.NAME, new Integer(OBJECT_NAME));
     }
 
     // used by the JNI code
     private int mNativeContext;
 
-    private native final void native_setup(MtpClient client, int queryType,
+    private native final void native_setup(PtpClient client, int queryType,
             int deviceID, long storageID, long objectID, int[] columns);
     private native final void native_finalize();
     private native void native_wait_for_event();
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index fa8d61b..e6e9bc2 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -17,6 +17,7 @@
 package android.media.videoeditor;

 

 import java.io.IOException;

+import java.util.ArrayList;

 import java.util.List;

 

 import android.graphics.Bitmap;

@@ -178,12 +179,11 @@
         // duration change.

         invalidateEndTransition();

 

-        final long oldDurationMs = mDurationMs;

         mDurationMs = durationMs;

 

         adjustTransitions();

-        adjustOverlays();

-        adjustEffects();

+        final List<Overlay> adjustedOverlays = adjustOverlays();

+        final List<Effect> adjustedEffects = adjustEffects();

 

         // Invalidate the beginning and end transitions after adjustments.

         // This invalidation is necessary for the case in which an effect or

@@ -191,11 +191,7 @@
         // before the setDuration reduces the duration of the media item and

         // causes an overlap of the beginning and/or end transition with the

         // effect.

-        // If the duration is growing, the begin transition does not need to

-        // be invalidated since the effects, overlays are not adjusted.

-        if (mDurationMs < oldDurationMs) {

-            invalidateBeginTransition();

-        }

+        invalidateBeginTransition(adjustedEffects, adjustedOverlays);

         invalidateEndTransition();

     }

 

@@ -293,14 +289,16 @@
     /**

      * Invalidate the begin transition if any effects and overlays overlap

      * with the begin transition.

+     *

+     * @param effects List of effects to check for transition overlap

+     * @param overlays List of overlays to check for transition overlap

      */

-    private void invalidateBeginTransition() {

+    private void invalidateBeginTransition(List<Effect> effects, List<Overlay> overlays) {

         if (mBeginTransition != null && mBeginTransition.isGenerated()) {

             final long transitionDurationMs = mBeginTransition.getDuration();

 

             // The begin transition must be invalidated if it overlaps with

             // an effect.

-            final List<Effect> effects = getAllEffects();

             for (Effect effect : effects) {

                 // Check if the effect overlaps with the begin transition

                 if (effect.getStartTime() < transitionDurationMs) {

@@ -312,7 +310,6 @@
             if (mBeginTransition.isGenerated()) {

                 // The end transition must be invalidated if it overlaps with

                 // an overlay.

-                final List<Overlay> overlays = getAllOverlays();

                 for (Overlay overlay : overlays) {

                     // Check if the overlay overlaps with the end transition

                     if (overlay.getStartTime() < transitionDurationMs) {

@@ -362,8 +359,11 @@
 

     /**

      * Adjust the start time and/or duration of effects.

+     *

+     * @return The list of effects which were adjusted

      */

-    private void adjustEffects() {

+    private List<Effect> adjustEffects() {

+        final List<Effect> adjustedEffects = new ArrayList<Effect>();

         final List<Effect> effects = getAllEffects();

         for (Effect effect : effects) {

             // Adjust the start time if necessary

@@ -385,14 +385,20 @@
             if (effectStartTimeMs != effect.getStartTime() ||

                     effectDurationMs != effect.getDuration()) {

                 effect.setStartTimeAndDuration(effectStartTimeMs, effectDurationMs);

+                adjustedEffects.add(effect);

             }

         }

+

+        return adjustedEffects;

     }

 

     /**

      * Adjust the start time and/or duration of overlays.

+     *

+     * @return The list of overlays which were adjusted

      */

-    private void adjustOverlays() {

+    private List<Overlay> adjustOverlays() {

+        final List<Overlay> adjustedOverlays = new ArrayList<Overlay>();

         final List<Overlay> overlays = getAllOverlays();

         for (Overlay overlay : overlays) {

             // Adjust the start time if necessary

@@ -414,8 +420,11 @@
             if (overlayStartTimeMs != overlay.getStartTime() ||

                     overlayDurationMs != overlay.getDuration()) {

                 overlay.setStartTimeAndDuration(overlayStartTimeMs, overlayDurationMs);

+                adjustedOverlays.add(overlay);

             }

         }

+

+        return adjustedOverlays;

     }

 

     /**

diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 25d243b..fbdfa67 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -9,10 +9,10 @@
     android_media_ResampleInputStream.cpp \
     android_media_MediaProfiles.cpp \
     android_media_AmrInputStream.cpp \
-	android_media_MtpClient.cpp \
-	android_media_MtpCursor.cpp \
 	android_media_MtpDatabase.cpp \
 	android_media_MtpServer.cpp \
+	android_media_PtpClient.cpp \
+	android_media_PtpCursor.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 997d017..28aef0c 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -774,8 +774,8 @@
 extern int register_android_media_MediaScanner(JNIEnv *env);
 extern int register_android_media_ResampleInputStream(JNIEnv *env);
 extern int register_android_media_MediaProfiles(JNIEnv *env);
-extern int register_android_media_MtpClient(JNIEnv *env);
-extern int register_android_media_MtpCursor(JNIEnv *env);
+extern int register_android_media_PtpClient(JNIEnv *env);
+extern int register_android_media_PtpCursor(JNIEnv *env);
 extern int register_android_media_MtpDatabase(JNIEnv *env);
 extern int register_android_media_MtpServer(JNIEnv *env);
 extern int register_android_media_AmrInputStream(JNIEnv *env);
@@ -826,13 +826,13 @@
         goto bail;
     }
 
-    if (register_android_media_MtpClient(env) < 0) {
-        LOGE("ERROR: MtpClient native registration failed");
+    if (register_android_media_PtpClient(env) < 0) {
+        LOGE("ERROR: PtpClient native registration failed");
         goto bail;
     }
 
-    if (register_android_media_MtpCursor(env) < 0) {
-        LOGE("ERROR: MtpCursor native registration failed");
+    if (register_android_media_PtpCursor(env) < 0) {
+        LOGE("ERROR: PtpCursor native registration failed");
         goto bail;
     }
 
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index 4525d1f..f04a2ae 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -948,18 +948,21 @@
             result = new MtpProperty(property, MTP_TYPE_UINT128);
             break;
         case MTP_PROPERTY_NAME:
-        case MTP_PROPERTY_DATE_MODIFIED:
         case MTP_PROPERTY_DISPLAY_NAME:
-        case MTP_PROPERTY_DATE_ADDED:
         case MTP_PROPERTY_ARTIST:
         case MTP_PROPERTY_ALBUM_NAME:
         case MTP_PROPERTY_ALBUM_ARTIST:
-        case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
         case MTP_PROPERTY_GENRE:
         case MTP_PROPERTY_COMPOSER:
         case MTP_PROPERTY_DESCRIPTION:
             result = new MtpProperty(property, MTP_TYPE_STR);
             break;
+        case MTP_PROPERTY_DATE_MODIFIED:
+        case MTP_PROPERTY_DATE_ADDED:
+        case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
+            result = new MtpProperty(property, MTP_TYPE_STR);
+            result->setFormDateTime();
+            break;
         case MTP_PROPERTY_OBJECT_FILE_NAME:
             // We allow renaming files and folders
             result = new MtpProperty(property, MTP_TYPE_STR, true);
diff --git a/media/jni/android_media_MtpClient.cpp b/media/jni/android_media_PtpClient.cpp
similarity index 80%
rename from media/jni/android_media_MtpClient.cpp
rename to media/jni/android_media_PtpClient.cpp
index 144dfc8..6af83e4 100644
--- a/media/jni/android_media_MtpClient.cpp
+++ b/media/jni/android_media_PtpClient.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "MtpClientJNI"
+#define LOG_TAG "PtpClientJNI"
 #include "utils/Log.h"
 
 #include <stdio.h>
@@ -103,7 +103,7 @@
 // ----------------------------------------------------------------------------
 
 static void
-android_media_MtpClient_setup(JNIEnv *env, jobject thiz)
+android_media_PtpClient_setup(JNIEnv *env, jobject thiz)
 {
 #ifdef HAVE_ANDROID_OS
     LOGD("setup\n");
@@ -114,7 +114,7 @@
 }
 
 static void
-android_media_MtpClient_finalize(JNIEnv *env, jobject thiz)
+android_media_PtpClient_finalize(JNIEnv *env, jobject thiz)
 {
 #ifdef HAVE_ANDROID_OS
     LOGD("finalize\n");
@@ -126,7 +126,7 @@
 }
 
 static jboolean
-android_media_MtpClient_start(JNIEnv *env, jobject thiz)
+android_media_PtpClient_start(JNIEnv *env, jobject thiz)
 {
 #ifdef HAVE_ANDROID_OS
     LOGD("start\n");
@@ -138,7 +138,7 @@
 }
 
 static void
-android_media_MtpClient_stop(JNIEnv *env, jobject thiz)
+android_media_PtpClient_stop(JNIEnv *env, jobject thiz)
 {
 #ifdef HAVE_ANDROID_OS
     LOGD("stop\n");
@@ -148,7 +148,7 @@
 }
 
 static jboolean
-android_media_MtpClient_delete_object(JNIEnv *env, jobject thiz,
+android_media_PtpClient_delete_object(JNIEnv *env, jobject thiz,
         jint device_id, jlong object_id)
 {
 #ifdef HAVE_ANDROID_OS
@@ -162,7 +162,7 @@
 }
 
 static jlong
-android_media_MtpClient_get_parent(JNIEnv *env, jobject thiz,
+android_media_PtpClient_get_parent(JNIEnv *env, jobject thiz,
         jint device_id, jlong object_id)
 {
 #ifdef HAVE_ANDROID_OS
@@ -176,7 +176,7 @@
 }
 
 static jlong
-android_media_MtpClient_get_storage_id(JNIEnv *env, jobject thiz,
+android_media_PtpClient_get_storage_id(JNIEnv *env, jobject thiz,
         jint device_id, jlong object_id)
 {
  #ifdef HAVE_ANDROID_OS
@@ -190,7 +190,7 @@
 }
 
 static jboolean
-android_media_MtpClient_import_file(JNIEnv *env, jobject thiz,
+android_media_PtpClient_import_file(JNIEnv *env, jobject thiz,
         jint device_id, jlong object_id, jstring dest_path)
 {
 #ifdef HAVE_ANDROID_OS
@@ -209,28 +209,28 @@
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
-    {"native_setup",            "()V",  (void *)android_media_MtpClient_setup},
-    {"native_finalize",         "()V",  (void *)android_media_MtpClient_finalize},
-    {"native_start",            "()Z",  (void *)android_media_MtpClient_start},
-    {"native_stop",             "()V",  (void *)android_media_MtpClient_stop},
-    {"native_delete_object",   "(IJ)Z", (void *)android_media_MtpClient_delete_object},
-    {"native_get_parent",      "(IJ)J", (void *)android_media_MtpClient_get_parent},
-    {"native_get_storage_id",  "(IJ)J", (void *)android_media_MtpClient_get_storage_id},
+    {"native_setup",            "()V",  (void *)android_media_PtpClient_setup},
+    {"native_finalize",         "()V",  (void *)android_media_PtpClient_finalize},
+    {"native_start",            "()Z",  (void *)android_media_PtpClient_start},
+    {"native_stop",             "()V",  (void *)android_media_PtpClient_stop},
+    {"native_delete_object",   "(IJ)Z", (void *)android_media_PtpClient_delete_object},
+    {"native_get_parent",      "(IJ)J", (void *)android_media_PtpClient_get_parent},
+    {"native_get_storage_id",  "(IJ)J", (void *)android_media_PtpClient_get_storage_id},
     {"native_import_file",     "(IJLjava/lang/String;)Z",
-                                        (void *)android_media_MtpClient_import_file},
+                                        (void *)android_media_PtpClient_import_file},
 };
 
-static const char* const kClassPathName = "android/media/MtpClient";
+static const char* const kClassPathName = "android/media/PtpClient";
 
-int register_android_media_MtpClient(JNIEnv *env)
+int register_android_media_PtpClient(JNIEnv *env)
 {
     jclass clazz;
 
-    LOGD("register_android_media_MtpClient\n");
+    LOGD("register_android_media_PtpClient\n");
 
-    clazz = env->FindClass("android/media/MtpClient");
+    clazz = env->FindClass("android/media/PtpClient");
     if (clazz == NULL) {
-        LOGE("Can't find android/media/MtpClient");
+        LOGE("Can't find android/media/PtpClient");
         return -1;
     }
     method_deviceAdded = env->GetMethodID(clazz, "deviceAdded", "(I)V");
@@ -245,10 +245,10 @@
     }
     field_context = env->GetFieldID(clazz, "mNativeContext", "I");
     if (field_context == NULL) {
-        LOGE("Can't find MtpClient.mNativeContext");
+        LOGE("Can't find PtpClient.mNativeContext");
         return -1;
     }
 
     return AndroidRuntime::registerNativeMethods(env,
-                "android/media/MtpClient", gMethods, NELEM(gMethods));
+                "android/media/PtpClient", gMethods, NELEM(gMethods));
 }
diff --git a/media/jni/android_media_MtpCursor.cpp b/media/jni/android_media_PtpCursor.cpp
similarity index 72%
rename from media/jni/android_media_MtpCursor.cpp
rename to media/jni/android_media_PtpCursor.cpp
index 7a0ae8a..76c88f6 100644
--- a/media/jni/android_media_MtpCursor.cpp
+++ b/media/jni/android_media_PtpCursor.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "MtpCursorJNI"
+#define LOG_TAG "PtpCursorJNI"
 #include "utils/Log.h"
 
 #include <stdio.h>
@@ -29,7 +29,7 @@
 #include "binder/CursorWindow.h"
 
 #include "MtpClient.h"
-#include "MtpCursor.h"
+#include "PtpCursor.h"
 
 using namespace android;
 
@@ -37,7 +37,7 @@
 
 static jfieldID field_context;
 
-// From android_media_MtpClient.cpp
+// From android_media_PtpClient.cpp
 MtpClient * get_client_from_object(JNIEnv * env, jobject javaClient);
 
 // ----------------------------------------------------------------------------
@@ -48,11 +48,11 @@
 }
 
 static void
-android_media_MtpCursor_setup(JNIEnv *env, jobject thiz, jobject javaClient,
+android_media_PtpCursor_setup(JNIEnv *env, jobject thiz, jobject javaClient,
         jint queryType, jint deviceID, jlong storageID, jlong objectID, jintArray javaColumns)
 {
 #ifdef HAVE_ANDROID_OS
-    LOGD("android_media_MtpCursor_setup queryType: %d deviceID: %d storageID: %lld objectID: %lld\n",
+    LOGD("android_media_PtpCursor_setup queryType: %d deviceID: %d storageID: %lld objectID: %lld\n",
                 queryType, deviceID, storageID, objectID);
 
     int* columns = NULL;
@@ -63,7 +63,7 @@
     }
 
     MtpClient* client = get_client_from_object(env, javaClient);
-    MtpCursor* cursor = new MtpCursor(client, queryType,
+    PtpCursor* cursor = new PtpCursor(client, queryType,
             deviceID, storageID, objectID, columnCount, columns);
 
     if (columns)
@@ -73,17 +73,17 @@
 }
 
 static void
-android_media_MtpCursor_finalize(JNIEnv *env, jobject thiz)
+android_media_PtpCursor_finalize(JNIEnv *env, jobject thiz)
 {
 #ifdef HAVE_ANDROID_OS
     LOGD("finalize\n");
-    MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context);
+    PtpCursor *cursor = (PtpCursor *)env->GetIntField(thiz, field_context);
     delete cursor;
 #endif
 }
 
 static jint
-android_media_MtpCursor_fill_window(JNIEnv *env, jobject thiz, jobject javaWindow, jint startPos)
+android_media_PtpCursor_fill_window(JNIEnv *env, jobject thiz, jobject javaWindow, jint startPos)
 {
 #ifdef HAVE_ANDROID_OS
     CursorWindow* window = get_window_from_object(env, javaWindow);
@@ -93,7 +93,7 @@
                           "Bad CursorWindow");
         return 0;
     }
-    MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context);
+    PtpCursor *cursor = (PtpCursor *)env->GetIntField(thiz, field_context);
 
     return cursor->fillWindow(window, startPos);
 #else
@@ -104,33 +104,33 @@
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
-    {"native_setup",            "(Landroid/media/MtpClient;IIJJ[I)V",
-                                        (void *)android_media_MtpCursor_setup},
-    {"native_finalize",         "()V",  (void *)android_media_MtpCursor_finalize},
+    {"native_setup",            "(Landroid/media/PtpClient;IIJJ[I)V",
+                                        (void *)android_media_PtpCursor_setup},
+    {"native_finalize",         "()V",  (void *)android_media_PtpCursor_finalize},
     {"native_fill_window",      "(Landroid/database/CursorWindow;I)I",
-                                        (void *)android_media_MtpCursor_fill_window},
+                                        (void *)android_media_PtpCursor_fill_window},
 
 };
 
-static const char* const kClassPathName = "android/media/MtpCursor";
+static const char* const kClassPathName = "android/media/PtpCursor";
 
-int register_android_media_MtpCursor(JNIEnv *env)
+int register_android_media_PtpCursor(JNIEnv *env)
 {
     jclass clazz;
 
-    LOGD("register_android_media_MtpCursor\n");
+    LOGD("register_android_media_PtpCursor\n");
 
-    clazz = env->FindClass("android/media/MtpCursor");
+    clazz = env->FindClass("android/media/PtpCursor");
     if (clazz == NULL) {
-        LOGE("Can't find android/media/MtpCursor");
+        LOGE("Can't find android/media/PtpCursor");
         return -1;
     }
     field_context = env->GetFieldID(clazz, "mNativeContext", "I");
     if (field_context == NULL) {
-        LOGE("Can't find MtpCursor.mNativeContext");
+        LOGE("Can't find PtpCursor.mNativeContext");
         return -1;
     }
 
     return AndroidRuntime::registerNativeMethods(env,
-                "android/media/MtpCursor", gMethods, NELEM(gMethods));
+                "android/media/PtpCursor", gMethods, NELEM(gMethods));
 }
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 3341ff7..a9d537f 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -31,7 +31,6 @@
 	libandroid_runtime    			\
 	libstagefright        			\
 	libstagefright_omx    			\
-	libstagefright_color_conversion         \
 	libstagefright_foundation               \
 	libsurfaceflinger_client
 
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 4ad1eb4..db23836 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -71,6 +71,7 @@
         libcrypto
 
 LOCAL_STATIC_LIBRARIES := \
+        libstagefright_color_conversion \
         libstagefright_aacdec \
         libstagefright_aacenc \
         libstagefright_amrnbdec \
@@ -97,7 +98,6 @@
         libstagefright_enc_common \
         libstagefright_avc_common \
         libstagefright_foundation \
-        libstagefright_color_conversion
 
 ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
         LOCAL_LDLIBS += -lpthread -ldl
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 922aaa8..ec58919 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -81,16 +81,8 @@
 
 struct AwesomeLocalRenderer : public AwesomeRenderer {
     AwesomeLocalRenderer(
-            OMX_COLOR_FORMATTYPE colorFormat,
-            const sp<Surface> &surface,
-            size_t displayWidth, size_t displayHeight,
-            size_t decodedWidth, size_t decodedHeight,
-            int32_t rotationDegrees)
-        : mTarget(NULL) {
-            init(colorFormat, surface,
-                 displayWidth, displayHeight,
-                 decodedWidth, decodedHeight,
-                 rotationDegrees);
+            const sp<Surface> &surface, const sp<MetaData> &meta)
+        : mTarget(new SoftwareRenderer(surface, meta)) {
     }
 
     virtual void render(MediaBuffer *buffer) {
@@ -111,28 +103,10 @@
 private:
     SoftwareRenderer *mTarget;
 
-    void init(
-            OMX_COLOR_FORMATTYPE colorFormat,
-            const sp<Surface> &surface,
-            size_t displayWidth, size_t displayHeight,
-            size_t decodedWidth, size_t decodedHeight,
-            int32_t rotationDegrees);
-
     AwesomeLocalRenderer(const AwesomeLocalRenderer &);
     AwesomeLocalRenderer &operator=(const AwesomeLocalRenderer &);;
 };
 
-void AwesomeLocalRenderer::init(
-        OMX_COLOR_FORMATTYPE colorFormat,
-        const sp<Surface> &surface,
-        size_t displayWidth, size_t displayHeight,
-        size_t decodedWidth, size_t decodedHeight,
-        int32_t rotationDegrees) {
-    mTarget = new SoftwareRenderer(
-            colorFormat, surface, displayWidth, displayHeight,
-            decodedWidth, decodedHeight, rotationDegrees);
-}
-
 struct AwesomeNativeWindowRenderer : public AwesomeRenderer {
     AwesomeNativeWindowRenderer(
             const sp<ANativeWindow> &nativeWindow,
@@ -188,9 +162,7 @@
       mAudioPlayer(NULL),
       mFlags(0),
       mExtractorFlags(0),
-      mLastVideoBuffer(NULL),
       mVideoBuffer(NULL),
-      mSuspensionState(NULL),
       mDecryptHandle(NULL) {
     CHECK_EQ(mClient.connect(), OK);
 
@@ -433,11 +405,6 @@
 
     mVideoRenderer.clear();
 
-    if (mLastVideoBuffer) {
-        mLastVideoBuffer->release();
-        mLastVideoBuffer = NULL;
-    }
-
     if (mVideoBuffer) {
         mVideoBuffer->release();
         mVideoBuffer = NULL;
@@ -469,7 +436,6 @@
     mDurationUs = -1;
     mFlags = 0;
     mExtractorFlags = 0;
-    mVideoWidth = mVideoHeight = -1;
     mTimeSourceDeltaUs = 0;
     mVideoTimeUs = 0;
 
@@ -482,9 +448,6 @@
 
     mFileSource.clear();
 
-    delete mSuspensionState;
-    mSuspensionState = NULL;
-
     mBitrate = -1;
 }
 
@@ -637,11 +600,6 @@
 
     mVideoRenderer.clear();
 
-    if (mLastVideoBuffer) {
-        mLastVideoBuffer->release();
-        mLastVideoBuffer = NULL;
-    }
-
     if (mVideoBuffer) {
         mVideoBuffer->release();
         mVideoBuffer = NULL;
@@ -749,6 +707,13 @@
 
     bool deferredAudioSeek = false;
 
+    if (mDecryptHandle != NULL) {
+        int64_t position;
+        getPosition(&position);
+        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                Playback::START, position / 1000);
+    }
+
     if (mAudioSource != NULL) {
         if (mAudioPlayer == NULL) {
             if (mAudioSink != NULL) {
@@ -766,6 +731,11 @@
 
                     mFlags &= ~(PLAYING | FIRST_FRAME);
 
+                    if (mDecryptHandle != NULL) {
+                        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                                 Playback::STOP, 0);
+                    }
+
                     return err;
                 }
 
@@ -802,22 +772,31 @@
         seekTo_l(0);
     }
 
-    if (mDecryptHandle != NULL) {
-        int64_t position;
-        getPosition(&position);
-        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
-                Playback::START, position / 1000);
-    }
-
     return OK;
 }
 
 void AwesomePlayer::notifyVideoSize_l() {
     sp<MetaData> meta = mVideoSource->getFormat();
 
-    int32_t decodedWidth, decodedHeight;
-    CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
-    CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
+    int32_t cropLeft, cropTop, cropRight, cropBottom;
+    if (!meta->findRect(
+                kKeyCropRect, &cropLeft, &cropTop, &cropRight, &cropBottom)) {
+        int32_t width, height;
+        CHECK(meta->findInt32(kKeyWidth, &width));
+        CHECK(meta->findInt32(kKeyHeight, &height));
+
+        cropLeft = cropTop = 0;
+        cropRight = width - 1;
+        cropBottom = height - 1;
+
+        LOGV("got dimensions only %d x %d", width, height);
+    } else {
+        LOGV("got crop rect %d, %d, %d, %d",
+             cropLeft, cropTop, cropRight, cropBottom);
+    }
+
+    int32_t usableWidth = cropRight - cropLeft + 1;
+    int32_t usableHeight = cropBottom - cropTop + 1;
 
     int32_t rotationDegrees;
     if (!mVideoTrack->getFormat()->findInt32(
@@ -827,10 +806,10 @@
 
     if (rotationDegrees == 90 || rotationDegrees == 270) {
         notifyListener_l(
-                MEDIA_SET_VIDEO_SIZE, decodedHeight, decodedWidth);
+                MEDIA_SET_VIDEO_SIZE, usableHeight, usableWidth);
     } else {
         notifyListener_l(
-                MEDIA_SET_VIDEO_SIZE, decodedWidth, decodedHeight);
+                MEDIA_SET_VIDEO_SIZE, usableWidth, usableHeight);
     }
 }
 
@@ -872,12 +851,7 @@
         // allocate their buffers in local address space.  This renderer
         // then performs a color conversion and copy to get the data
         // into the ANativeBuffer.
-        mVideoRenderer = new AwesomeLocalRenderer(
-            (OMX_COLOR_FORMATTYPE)format,
-            mSurface,
-            mVideoWidth, mVideoHeight,
-            decodedWidth, decodedHeight,
-            rotationDegrees);
+        mVideoRenderer = new AwesomeLocalRenderer(mSurface, meta);
     }
 }
 
@@ -1041,20 +1015,6 @@
     }
 }
 
-status_t AwesomePlayer::getVideoDimensions(
-        int32_t *width, int32_t *height) const {
-    Mutex::Autolock autoLock(mLock);
-
-    if (mVideoWidth < 0 || mVideoHeight < 0) {
-        return UNKNOWN_ERROR;
-    }
-
-    *width = mVideoWidth;
-    *height = mVideoHeight;
-
-    return OK;
-}
-
 void AwesomePlayer::setAudioSource(sp<MediaSource> source) {
     CHECK(source != NULL);
 
@@ -1123,9 +1083,6 @@
             }
         }
 
-        CHECK(mVideoTrack->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
-        CHECK(mVideoTrack->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
-
         status_t err = mVideoSource->start();
 
         if (err != OK) {
@@ -1180,11 +1137,6 @@
     mVideoEventPending = false;
 
     if (mSeeking) {
-        if (mLastVideoBuffer) {
-            mLastVideoBuffer->release();
-            mLastVideoBuffer = NULL;
-        }
-
         if (mVideoBuffer) {
             mVideoBuffer->release();
             mVideoBuffer = NULL;
@@ -1327,11 +1279,7 @@
         mVideoRenderer->render(mVideoBuffer);
     }
 
-    if (mLastVideoBuffer) {
-        mLastVideoBuffer->release();
-        mLastVideoBuffer = NULL;
-    }
-    mLastVideoBuffer = mVideoBuffer;
+    mVideoBuffer->release();
     mVideoBuffer = NULL;
 
     postVideoEvent_l();
@@ -1745,142 +1693,6 @@
     mPreparedCondition.broadcast();
 }
 
-status_t AwesomePlayer::suspend() {
-    LOGV("suspend");
-    Mutex::Autolock autoLock(mLock);
-
-    if (mSuspensionState != NULL) {
-        if (mLastVideoBuffer == NULL) {
-            //go into here if video is suspended again
-            //after resuming without being played between
-            //them
-            SuspensionState *state = mSuspensionState;
-            mSuspensionState = NULL;
-            reset_l();
-            mSuspensionState = state;
-            return OK;
-        }
-
-        delete mSuspensionState;
-        mSuspensionState = NULL;
-    }
-
-    if (mFlags & PREPARING) {
-        mFlags |= PREPARE_CANCELLED;
-        if (mConnectingDataSource != NULL) {
-            LOGI("interrupting the connection process");
-            mConnectingDataSource->disconnect();
-        }
-    }
-
-    while (mFlags & PREPARING) {
-        mPreparedCondition.wait(mLock);
-    }
-
-    SuspensionState *state = new SuspensionState;
-    state->mUri = mUri;
-    state->mUriHeaders = mUriHeaders;
-    state->mFileSource = mFileSource;
-
-    state->mFlags = mFlags & (PLAYING | AUTO_LOOPING | LOOPING | AT_EOS);
-    getPosition(&state->mPositionUs);
-
-    if (mLastVideoBuffer) {
-        size_t size = mLastVideoBuffer->range_length();
-
-        if (size) {
-            int32_t unreadable;
-            if (!mLastVideoBuffer->meta_data()->findInt32(
-                        kKeyIsUnreadable, &unreadable)
-                    || unreadable == 0) {
-                state->mLastVideoFrameSize = size;
-                state->mLastVideoFrame = malloc(size);
-                memcpy(state->mLastVideoFrame,
-                       (const uint8_t *)mLastVideoBuffer->data()
-                            + mLastVideoBuffer->range_offset(),
-                       size);
-
-                state->mVideoWidth = mVideoWidth;
-                state->mVideoHeight = mVideoHeight;
-
-                sp<MetaData> meta = mVideoSource->getFormat();
-                CHECK(meta->findInt32(kKeyColorFormat, &state->mColorFormat));
-                CHECK(meta->findInt32(kKeyWidth, &state->mDecodedWidth));
-                CHECK(meta->findInt32(kKeyHeight, &state->mDecodedHeight));
-            } else {
-                LOGV("Unable to save last video frame, we have no access to "
-                     "the decoded video data.");
-            }
-        }
-    }
-
-    reset_l();
-
-    mSuspensionState = state;
-
-    return OK;
-}
-
-status_t AwesomePlayer::resume() {
-    LOGV("resume");
-    Mutex::Autolock autoLock(mLock);
-
-    if (mSuspensionState == NULL) {
-        return INVALID_OPERATION;
-    }
-
-    SuspensionState *state = mSuspensionState;
-    mSuspensionState = NULL;
-
-    status_t err;
-    if (state->mFileSource != NULL) {
-        err = setDataSource_l(state->mFileSource);
-
-        if (err == OK) {
-            mFileSource = state->mFileSource;
-        }
-    } else {
-        err = setDataSource_l(state->mUri, &state->mUriHeaders);
-    }
-
-    if (err != OK) {
-        delete state;
-        state = NULL;
-
-        return err;
-    }
-
-    seekTo_l(state->mPositionUs);
-
-    mFlags = state->mFlags & (AUTO_LOOPING | LOOPING | AT_EOS);
-
-    if (state->mLastVideoFrame && mSurface != NULL) {
-        mVideoRenderer =
-            new AwesomeLocalRenderer(
-                    (OMX_COLOR_FORMATTYPE)state->mColorFormat,
-                    mSurface,
-                    state->mVideoWidth,
-                    state->mVideoHeight,
-                    state->mDecodedWidth,
-                    state->mDecodedHeight,
-                    0);
-
-        mVideoRendererIsPreview = true;
-
-        ((AwesomeLocalRenderer *)mVideoRenderer.get())->render(
-                state->mLastVideoFrame, state->mLastVideoFrameSize);
-    }
-
-    if (state->mFlags & PLAYING) {
-        play_l();
-    }
-
-    mSuspensionState = state;
-    state = NULL;
-
-    return OK;
-}
-
 uint32_t AwesomePlayer::flags() const {
     return mExtractorFlags;
 }
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
index 63b476e..884f3b4 100644
--- a/media/libstagefright/MetaData.cpp
+++ b/media/libstagefright/MetaData.cpp
@@ -70,6 +70,19 @@
     return setData(key, TYPE_POINTER, &value, sizeof(value));
 }
 
+bool MetaData::setRect(
+        uint32_t key,
+        int32_t left, int32_t top,
+        int32_t right, int32_t bottom) {
+    Rect r;
+    r.mLeft = left;
+    r.mTop = top;
+    r.mRight = right;
+    r.mBottom = bottom;
+
+    return setData(key, TYPE_RECT, &r, sizeof(r));
+}
+
 bool MetaData::findCString(uint32_t key, const char **value) {
     uint32_t type;
     const void *data;
@@ -143,6 +156,28 @@
     return true;
 }
 
+bool MetaData::findRect(
+        uint32_t key,
+        int32_t *left, int32_t *top,
+        int32_t *right, int32_t *bottom) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_RECT) {
+        return false;
+    }
+
+    CHECK_EQ(size, sizeof(Rect));
+
+    const Rect *r = (const Rect *)data;
+    *left = r->mLeft;
+    *top = r->mTop;
+    *right = r->mRight;
+    *bottom = r->mBottom;
+
+    return true;
+}
+
 bool MetaData::setData(
         uint32_t key, uint32_t type, const void *data, size_t size) {
     bool overwrote_existing = true;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 6ca0f4f..3108e4e 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -38,11 +38,11 @@
 #include <binder/IServiceManager.h>
 #include <binder/MemoryDealer.h>
 #include <binder/ProcessState.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/IMediaPlayerService.h>
 #include <media/stagefright/HardwareAPI.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MetaData.h>
@@ -526,7 +526,7 @@
         size_t size;
         if (meta->findData(kKeyESDS, &type, &data, &size)) {
             ESDS esds((const char *)data, size);
-            CHECK_EQ(esds.InitCheck(), OK);
+            CHECK_EQ(esds.InitCheck(), (status_t)OK);
 
             const void *codec_specific_data;
             size_t codec_specific_data_size;
@@ -541,7 +541,7 @@
             const uint8_t *ptr = (const uint8_t *)data;
 
             CHECK(size >= 7);
-            CHECK_EQ(ptr[0], 1);  // configurationVersion == 1
+            CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
             uint8_t profile = ptr[1];
             uint8_t level = ptr[3];
 
@@ -730,7 +730,7 @@
 
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     if ((portIndex == kPortIndexInput && (mQuirks & kInputBufferSizesAreBogus))
         || (def.nBufferSize < size)) {
@@ -739,11 +739,11 @@
 
     err = mOMX->setParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     // Make sure the setting actually stuck.
     if (portIndex == kPortIndexInput
@@ -923,7 +923,7 @@
     }
 
     OMX_COLOR_FORMATTYPE colorFormat;
-    CHECK_EQ(OK, findTargetColorFormat(meta, &colorFormat));
+    CHECK_EQ((status_t)OK, findTargetColorFormat(meta, &colorFormat));
 
     status_t err;
     OMX_PARAM_PORTDEFINITIONTYPE def;
@@ -932,19 +932,19 @@
     //////////////////////// Input port /////////////////////////
     CHECK_EQ(setVideoPortFormatType(
             kPortIndexInput, OMX_VIDEO_CodingUnused,
-            colorFormat), OK);
+            colorFormat), (status_t)OK);
 
     InitOMXParams(&def);
     def.nPortIndex = kPortIndexInput;
 
     err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     def.nBufferSize = getFrameSize(colorFormat,
             stride > 0? stride: -stride, sliceHeight);
 
-    CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+    CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
 
     video_def->nFrameWidth = width;
     video_def->nFrameHeight = height;
@@ -956,20 +956,20 @@
 
     err = mOMX->setParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     //////////////////////// Output port /////////////////////////
     CHECK_EQ(setVideoPortFormatType(
             kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused),
-            OK);
+            (status_t)OK);
     InitOMXParams(&def);
     def.nPortIndex = kPortIndexOutput;
 
     err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
 
-    CHECK_EQ(err, OK);
-    CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+    CHECK_EQ(err, (status_t)OK);
+    CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
 
     video_def->nFrameWidth = width;
     video_def->nFrameHeight = height;
@@ -984,23 +984,23 @@
 
     err = mOMX->setParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     /////////////////// Codec-specific ////////////////////////
     switch (compressionFormat) {
         case OMX_VIDEO_CodingMPEG4:
         {
-            CHECK_EQ(setupMPEG4EncoderParameters(meta), OK);
+            CHECK_EQ(setupMPEG4EncoderParameters(meta), (status_t)OK);
             break;
         }
 
         case OMX_VIDEO_CodingH263:
-            CHECK_EQ(setupH263EncoderParameters(meta), OK);
+            CHECK_EQ(setupH263EncoderParameters(meta), (status_t)OK);
             break;
 
         case OMX_VIDEO_CodingAVC:
         {
-            CHECK_EQ(setupAVCEncoderParameters(meta), OK);
+            CHECK_EQ(setupAVCEncoderParameters(meta), (status_t)OK);
             break;
         }
 
@@ -1059,7 +1059,7 @@
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamVideoBitrate,
             &bitrateType, sizeof(bitrateType));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     bitrateType.eControlRate = OMX_Video_ControlRateVariable;
     bitrateType.nTargetBitrate = bitRate;
@@ -1067,7 +1067,7 @@
     err = mOMX->setParameter(
             mNode, OMX_IndexParamVideoBitrate,
             &bitrateType, sizeof(bitrateType));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
     return OK;
 }
 
@@ -1132,7 +1132,7 @@
 
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     h263type.nAllowedPictureTypes =
         OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
@@ -1159,10 +1159,10 @@
 
     err = mOMX->setParameter(
             mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
-    CHECK_EQ(setupBitRate(bitRate), OK);
-    CHECK_EQ(setupErrorCorrectionParameters(), OK);
+    CHECK_EQ(setupBitRate(bitRate), (status_t)OK);
+    CHECK_EQ(setupErrorCorrectionParameters(), (status_t)OK);
 
     return OK;
 }
@@ -1179,7 +1179,7 @@
 
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     mpeg4type.nSliceHeaderSpacing = 0;
     mpeg4type.bSVH = OMX_FALSE;
@@ -1211,10 +1211,10 @@
 
     err = mOMX->setParameter(
             mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
-    CHECK_EQ(setupBitRate(bitRate), OK);
-    CHECK_EQ(setupErrorCorrectionParameters(), OK);
+    CHECK_EQ(setupBitRate(bitRate), (status_t)OK);
+    CHECK_EQ(setupErrorCorrectionParameters(), (status_t)OK);
 
     return OK;
 }
@@ -1232,7 +1232,7 @@
 
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     h264type.nAllowedPictureTypes =
         OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
@@ -1284,9 +1284,9 @@
 
     err = mOMX->setParameter(
             mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
-    CHECK_EQ(setupBitRate(bitRate), OK);
+    CHECK_EQ(setupBitRate(bitRate), (status_t)OK);
 
     return OK;
 }
@@ -1324,8 +1324,8 @@
         status_t err = mOMX->getParameter(
                 mNode, OMX_IndexParamVideoPortFormat,
                 &format, sizeof(format));
-        CHECK_EQ(err, OK);
-        CHECK_EQ(format.eCompressionFormat, OMX_VIDEO_CodingUnused);
+        CHECK_EQ(err, (status_t)OK);
+        CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
 
         static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
 
@@ -1353,7 +1353,7 @@
     err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
 
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
 #if 1
     // XXX Need a (much) better heuristic to compute input buffer sizes.
@@ -1363,7 +1363,7 @@
     }
 #endif
 
-    CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+    CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
 
     video_def->nFrameWidth = width;
     video_def->nFrameHeight = height;
@@ -1385,8 +1385,8 @@
 
     err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
-    CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+    CHECK_EQ(err, (status_t)OK);
+    CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
 
 #if 0
     def.nBufferSize =
@@ -1510,7 +1510,7 @@
     CHECK(mState == LOADED || mState == ERROR);
 
     status_t err = mOMX->freeNode(mNode);
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     mNode = NULL;
     setState(DEAD);
@@ -1527,21 +1527,21 @@
 status_t OMXCodec::init() {
     // mLock is held.
 
-    CHECK_EQ(mState, LOADED);
+    CHECK_EQ((int)mState, (int)LOADED);
 
     status_t err;
     if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) {
         err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
-        CHECK_EQ(err, OK);
+        CHECK_EQ(err, (status_t)OK);
         setState(LOADED_TO_IDLE);
     }
 
     err = allocateBuffers();
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {
         err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
-        CHECK_EQ(err, OK);
+        CHECK_EQ(err, (status_t)OK);
 
         setState(LOADED_TO_IDLE);
     }
@@ -1896,10 +1896,10 @@
                 CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
 
                 status_t err = freeBuffer(kPortIndexInput, i);
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
             } else if (mState != ERROR
                     && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
-                CHECK_EQ(mPortStatus[kPortIndexInput], ENABLED);
+                CHECK_EQ((int)mPortStatus[kPortIndexInput], (int)ENABLED);
                 drainInputBuffer(&buffers->editItemAt(i));
             }
             break;
@@ -1937,7 +1937,7 @@
                 CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
 
                 status_t err = freeBuffer(kPortIndexOutput, i);
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
 
 #if 0
             } else if (mPortStatus[kPortIndexOutput] == ENABLED
@@ -1947,7 +1947,7 @@
                 mBufferFilled.signal();
 #endif
             } else if (mPortStatus[kPortIndexOutput] != SHUTTING_DOWN) {
-                CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
+                CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)ENABLED);
 
                 if (info->mMediaBuffer == NULL) {
                     CHECK(mOMXLivesLocally);
@@ -2045,84 +2045,6 @@
     }
 }
 
-void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
-    switch (event) {
-        case OMX_EventCmdComplete:
-        {
-            onCmdComplete((OMX_COMMANDTYPE)data1, data2);
-            break;
-        }
-
-        case OMX_EventError:
-        {
-            CODEC_LOGE("ERROR(0x%08lx, %ld)", data1, data2);
-
-            setState(ERROR);
-            break;
-        }
-
-        case OMX_EventPortSettingsChanged:
-        {
-            CODEC_LOGV("OMX_EventPortSettingsChanged(port=%ld, data2=0x%08lx)",
-                       data1, data2);
-
-            if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
-                onPortSettingsChanged(data1);
-            } else if (data1 == kPortIndexOutput
-                    && data2 == OMX_IndexConfigCommonOutputCrop) {
-
-                OMX_CONFIG_RECTTYPE rect;
-                rect.nPortIndex = kPortIndexOutput;
-                InitOMXParams(&rect);
-
-                status_t err =
-                         mOMX->getConfig(
-                             mNode, OMX_IndexConfigCommonOutputCrop,
-                             &rect, sizeof(rect));
-
-                if (err == OK) {
-                    CODEC_LOGV(
-                            "output crop (%ld, %ld, %ld, %ld)",
-                            rect.nLeft, rect.nTop, rect.nWidth, rect.nHeight);
-
-                    if (mNativeWindow != NULL) {
-                        android_native_rect_t crop;
-                        crop.left = rect.nLeft;
-                        crop.top = rect.nTop;
-                        crop.right = crop.left + rect.nWidth - 1;
-                        crop.bottom = crop.top + rect.nHeight - 1;
-
-                        CHECK_EQ(0, native_window_set_crop(
-                                    mNativeWindow.get(), &crop));
-                    }
-                } else {
-                    CODEC_LOGE("getConfig(OMX_IndexConfigCommonOutputCrop) "
-                               "returned error 0x%08x", err);
-                }
-            }
-            break;
-        }
-
-#if 0
-        case OMX_EventBufferFlag:
-        {
-            CODEC_LOGV("EVENT_BUFFER_FLAG(%ld)", data1);
-
-            if (data1 == kPortIndexOutput) {
-                mNoMoreOutputData = true;
-            }
-            break;
-        }
-#endif
-
-        default:
-        {
-            CODEC_LOGV("EVENT(%d, %ld, %ld)", event, data1, data2);
-            break;
-        }
-    }
-}
-
 // Has the format changed in any way that the client would have to be aware of?
 static bool formatHasNotablyChanged(
         const sp<MetaData> &from, const sp<MetaData> &to) {
@@ -2167,6 +2089,21 @@
         if (height_from != height_to) {
             return true;
         }
+
+        int32_t left_from, top_from, right_from, bottom_from;
+        CHECK(from->findRect(
+                    kKeyCropRect,
+                    &left_from, &top_from, &right_from, &bottom_from));
+
+        int32_t left_to, top_to, right_to, bottom_to;
+        CHECK(to->findRect(
+                    kKeyCropRect,
+                    &left_to, &top_to, &right_to, &bottom_to));
+
+        if (left_to != left_from || top_to != top_from
+                || right_to != right_from || bottom_to != bottom_from) {
+            return true;
+        }
     } else if (!strcasecmp(mime_from, MEDIA_MIMETYPE_AUDIO_RAW)) {
         int32_t numChannels_from, numChannels_to;
         CHECK(from->findInt32(kKeyChannelCount, &numChannels_from));
@@ -2188,6 +2125,78 @@
     return false;
 }
 
+void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            onCmdComplete((OMX_COMMANDTYPE)data1, data2);
+            break;
+        }
+
+        case OMX_EventError:
+        {
+            CODEC_LOGE("ERROR(0x%08lx, %ld)", data1, data2);
+
+            setState(ERROR);
+            break;
+        }
+
+        case OMX_EventPortSettingsChanged:
+        {
+            CODEC_LOGV("OMX_EventPortSettingsChanged(port=%ld, data2=0x%08lx)",
+                       data1, data2);
+
+            if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
+                onPortSettingsChanged(data1);
+            } else if (data1 == kPortIndexOutput
+                    && data2 == OMX_IndexConfigCommonOutputCrop) {
+
+                sp<MetaData> oldOutputFormat = mOutputFormat;
+                initOutputFormat(mSource->getFormat());
+
+                if (formatHasNotablyChanged(oldOutputFormat, mOutputFormat)) {
+                    mOutputPortSettingsHaveChanged = true;
+
+                    if (mNativeWindow != NULL) {
+                        int32_t left, top, right, bottom;
+                        CHECK(mOutputFormat->findRect(
+                                    kKeyCropRect,
+                                    &left, &top, &right, &bottom));
+
+                        android_native_rect_t crop;
+                        crop.left = left;
+                        crop.top = top;
+                        crop.right = right;
+                        crop.bottom = bottom;
+
+                        CHECK_EQ(0, native_window_set_crop(
+                                    mNativeWindow.get(), &crop));
+                    }
+                }
+            }
+            break;
+        }
+
+#if 0
+        case OMX_EventBufferFlag:
+        {
+            CODEC_LOGV("EVENT_BUFFER_FLAG(%ld)", data1);
+
+            if (data1 == kPortIndexOutput) {
+                mNoMoreOutputData = true;
+            }
+            break;
+        }
+#endif
+
+        default:
+        {
+            CODEC_LOGV("EVENT(%d, %ld, %ld)", event, data1, data2);
+            break;
+        }
+    }
+}
+
 void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
     switch (cmd) {
         case OMX_CommandStateSet:
@@ -2202,13 +2211,13 @@
             CODEC_LOGV("PORT_DISABLED(%ld)", portIndex);
 
             CHECK(mState == EXECUTING || mState == RECONFIGURING);
-            CHECK_EQ(mPortStatus[portIndex], DISABLING);
-            CHECK_EQ(mPortBuffers[portIndex].size(), 0);
+            CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLING);
+            CHECK_EQ(mPortBuffers[portIndex].size(), 0u);
 
             mPortStatus[portIndex] = DISABLED;
 
             if (mState == RECONFIGURING) {
-                CHECK_EQ(portIndex, kPortIndexOutput);
+                CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
 
                 sp<MetaData> oldOutputFormat = mOutputFormat;
                 initOutputFormat(mSource->getFormat());
@@ -2222,7 +2231,7 @@
                 enablePortAsync(portIndex);
 
                 status_t err = allocateBuffersOnPort(portIndex);
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
             }
             break;
         }
@@ -2233,12 +2242,12 @@
             CODEC_LOGV("PORT_ENABLED(%ld)", portIndex);
 
             CHECK(mState == EXECUTING || mState == RECONFIGURING);
-            CHECK_EQ(mPortStatus[portIndex], ENABLING);
+            CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLING);
 
             mPortStatus[portIndex] = ENABLED;
 
             if (mState == RECONFIGURING) {
-                CHECK_EQ(portIndex, kPortIndexOutput);
+                CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
 
                 setState(EXECUTING);
 
@@ -2253,14 +2262,14 @@
 
             CODEC_LOGV("FLUSH_DONE(%ld)", portIndex);
 
-            CHECK_EQ(mPortStatus[portIndex], SHUTTING_DOWN);
+            CHECK_EQ((int)mPortStatus[portIndex], (int)SHUTTING_DOWN);
             mPortStatus[portIndex] = ENABLED;
 
             CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]),
                      mPortBuffers[portIndex].size());
 
             if (mState == RECONFIGURING) {
-                CHECK_EQ(portIndex, kPortIndexOutput);
+                CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
 
                 disablePortAsync(portIndex);
             } else if (mState == EXECUTING_TO_IDLE) {
@@ -2274,7 +2283,7 @@
 
                     status_t err =
                         mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
-                    CHECK_EQ(err, OK);
+                    CHECK_EQ(err, (status_t)OK);
                 }
             } else {
                 // We're flushing both ports in preparation for seeking.
@@ -2314,11 +2323,11 @@
                 status_t err = mOMX->sendCommand(
                         mNode, OMX_CommandStateSet, OMX_StateExecuting);
 
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
 
                 setState(IDLE_TO_EXECUTING);
             } else {
-                CHECK_EQ(mState, EXECUTING_TO_IDLE);
+                CHECK_EQ((int)mState, (int)EXECUTING_TO_IDLE);
 
                 CHECK_EQ(
                     countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
@@ -2331,13 +2340,13 @@
                 status_t err = mOMX->sendCommand(
                         mNode, OMX_CommandStateSet, OMX_StateLoaded);
 
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
 
                 err = freeBuffersOnPort(kPortIndexInput);
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
 
                 err = freeBuffersOnPort(kPortIndexOutput);
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
 
                 mPortStatus[kPortIndexInput] = ENABLED;
                 mPortStatus[kPortIndexOutput] = ENABLED;
@@ -2349,7 +2358,7 @@
 
         case OMX_StateExecuting:
         {
-            CHECK_EQ(mState, IDLE_TO_EXECUTING);
+            CHECK_EQ((int)mState, (int)IDLE_TO_EXECUTING);
 
             CODEC_LOGV("Now Executing.");
 
@@ -2365,7 +2374,7 @@
 
         case OMX_StateLoaded:
         {
-            CHECK_EQ(mState, IDLE_TO_LOADED);
+            CHECK_EQ((int)mState, (int)IDLE_TO_LOADED);
 
             CODEC_LOGV("Now Loaded.");
 
@@ -2412,7 +2421,7 @@
             continue;
         }
 
-        CHECK_EQ(info->mOwnedByComponent, false);
+        CHECK_EQ((int)info->mOwnedByComponent, (int)false);
 
         CODEC_LOGV("freeing buffer %p on port %ld", info->mBuffer, portIndex);
 
@@ -2437,7 +2446,7 @@
     status_t err = mOMX->freeBuffer(mNode, portIndex, info->mBuffer);
 
     if (err == OK && info->mMediaBuffer != NULL) {
-        CHECK_EQ(portIndex, kPortIndexOutput);
+        CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
         info->mMediaBuffer->setObserver(NULL);
 
         // Make sure nobody but us owns this buffer at this point.
@@ -2463,8 +2472,8 @@
 void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) {
     CODEC_LOGV("PORT_SETTINGS_CHANGED(%ld)", portIndex);
 
-    CHECK_EQ(mState, EXECUTING);
-    CHECK_EQ(portIndex, kPortIndexOutput);
+    CHECK_EQ((int)mState, (int)EXECUTING);
+    CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
     setState(RECONFIGURING);
 
     if (mQuirks & kNeedsFlushBeforeDisable) {
@@ -2484,7 +2493,7 @@
          portIndex, countBuffersWeOwn(mPortBuffers[portIndex]),
          mPortBuffers[portIndex].size());
 
-    CHECK_EQ(mPortStatus[portIndex], ENABLED);
+    CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED);
     mPortStatus[portIndex] = SHUTTING_DOWN;
 
     if ((mQuirks & kRequiresFlushCompleteEmulation)
@@ -2498,7 +2507,7 @@
 
     status_t err =
         mOMX->sendCommand(mNode, OMX_CommandFlush, portIndex);
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     return true;
 }
@@ -2506,13 +2515,13 @@
 void OMXCodec::disablePortAsync(OMX_U32 portIndex) {
     CHECK(mState == EXECUTING || mState == RECONFIGURING);
 
-    CHECK_EQ(mPortStatus[portIndex], ENABLED);
+    CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED);
     mPortStatus[portIndex] = DISABLING;
 
     CODEC_LOGV("sending OMX_CommandPortDisable(%ld)", portIndex);
     status_t err =
         mOMX->sendCommand(mNode, OMX_CommandPortDisable, portIndex);
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     freeBuffersOnPort(portIndex, true);
 }
@@ -2520,17 +2529,17 @@
 void OMXCodec::enablePortAsync(OMX_U32 portIndex) {
     CHECK(mState == EXECUTING || mState == RECONFIGURING);
 
-    CHECK_EQ(mPortStatus[portIndex], DISABLED);
+    CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLED);
     mPortStatus[portIndex] = ENABLING;
 
     CODEC_LOGV("sending OMX_CommandPortEnable(%ld)", portIndex);
     status_t err =
         mOMX->sendCommand(mNode, OMX_CommandPortEnable, portIndex);
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 }
 
 void OMXCodec::fillOutputBuffers() {
-    CHECK_EQ(mState, EXECUTING);
+    CHECK_EQ((int)mState, (int)EXECUTING);
 
     // This is a workaround for some decoders not properly reporting
     // end-of-output-stream. If we own all input buffers and also own
@@ -2566,7 +2575,7 @@
 }
 
 void OMXCodec::drainInputBuffer(BufferInfo *info) {
-    CHECK_EQ(info->mOwnedByComponent, false);
+    CHECK_EQ((int)info->mOwnedByComponent, (int)false);
 
     if (mSignalledEOS) {
         return;
@@ -2603,7 +2612,7 @@
                 mNode, info->mBuffer, 0, size,
                 OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,
                 0);
-        CHECK_EQ(err, OK);
+        CHECK_EQ(err, (status_t)OK);
 
         info->mOwnedByComponent = true;
 
@@ -2771,7 +2780,7 @@
 }
 
 void OMXCodec::fillOutputBuffer(BufferInfo *info) {
-    CHECK_EQ(info->mOwnedByComponent, false);
+    CHECK_EQ((int)info->mOwnedByComponent, (int)false);
 
     if (mNoMoreOutputData) {
         CODEC_LOGV("There is no more output data available, not "
@@ -2779,19 +2788,21 @@
         return;
     }
 
-    sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer();
-    if (graphicBuffer != 0) {
-        // When using a native buffer we need to lock the buffer before giving
-        // it to OMX.
-        CHECK(!info->mOwnedByNativeWindow);
-        CODEC_LOGV("Calling lockBuffer on %p", info->mBuffer);
-        int err = mNativeWindow->lockBuffer(mNativeWindow.get(),
-                graphicBuffer.get());
-        if (err != 0) {
-            CODEC_LOGE("lockBuffer failed w/ error 0x%08x", err);
+    if (info->mMediaBuffer != NULL) {
+        sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer();
+        if (graphicBuffer != 0) {
+            // When using a native buffer we need to lock the buffer before
+            // giving it to OMX.
+            CHECK(!info->mOwnedByNativeWindow);
+            CODEC_LOGV("Calling lockBuffer on %p", info->mBuffer);
+            int err = mNativeWindow->lockBuffer(mNativeWindow.get(),
+                    graphicBuffer.get());
+            if (err != 0) {
+                CODEC_LOGE("lockBuffer failed w/ error 0x%08x", err);
 
-            setState(ERROR);
-            return;
+                setState(ERROR);
+                return;
+            }
         }
     }
 
@@ -2850,10 +2861,10 @@
     def.nPortIndex = portIndex;
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
     def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
     CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition,
-            &def, sizeof(def)), OK);
+            &def, sizeof(def)), (status_t)OK);
 
     // pcm param
     OMX_AUDIO_PARAM_PCMMODETYPE pcmParams;
@@ -2863,7 +2874,7 @@
     err = mOMX->getParameter(
             mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
 
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     pcmParams.nChannels = numChannels;
     pcmParams.eNumData = OMX_NumericalDataSigned;
@@ -2884,7 +2895,7 @@
     err = mOMX->setParameter(
             mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
 
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 }
 
 static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate(bool isAMRWB, int32_t bps) {
@@ -2941,13 +2952,13 @@
     status_t err =
         mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
 
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
 
     def.eAMRBandMode = pickModeFromBitRate(isWAMR, bitRate);
     err = mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     ////////////////////////
 
@@ -2976,33 +2987,33 @@
         status_t err = OMX_ErrorNone;
         while (OMX_ErrorNone == err) {
             CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioPortFormat,
-                    &format, sizeof(format)), OK);
+                    &format, sizeof(format)), (status_t)OK);
             if (format.eEncoding == OMX_AUDIO_CodingAAC) {
                 break;
             }
             format.nIndex++;
         }
-        CHECK_EQ(OK, err);
+        CHECK_EQ((status_t)OK, err);
         CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioPortFormat,
-                &format, sizeof(format)), OK);
+                &format, sizeof(format)), (status_t)OK);
 
         // port definition
         OMX_PARAM_PORTDEFINITIONTYPE def;
         InitOMXParams(&def);
         def.nPortIndex = kPortIndexOutput;
         CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamPortDefinition,
-                &def, sizeof(def)), OK);
+                &def, sizeof(def)), (status_t)OK);
         def.format.audio.bFlagErrorConcealment = OMX_TRUE;
         def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
         CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition,
-                &def, sizeof(def)), OK);
+                &def, sizeof(def)), (status_t)OK);
 
         // profile
         OMX_AUDIO_PARAM_AACPROFILETYPE profile;
         InitOMXParams(&profile);
         profile.nPortIndex = kPortIndexOutput;
         CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioAac,
-                &profile, sizeof(profile)), OK);
+                &profile, sizeof(profile)), (status_t)OK);
         profile.nChannels = numChannels;
         profile.eChannelMode = (numChannels == 1?
                 OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo);
@@ -3015,7 +3026,7 @@
         profile.eAACProfile = OMX_AUDIO_AACObjectLC;
         profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
         CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioAac,
-                &profile, sizeof(profile)), OK);
+                &profile, sizeof(profile)), (status_t)OK);
 
     } else {
         OMX_AUDIO_PARAM_AACPROFILETYPE profile;
@@ -3024,7 +3035,7 @@
 
         status_t err = mOMX->getParameter(
                 mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
-        CHECK_EQ(err, OK);
+        CHECK_EQ(err, (status_t)OK);
 
         profile.nChannels = numChannels;
         profile.nSampleRate = sampleRate;
@@ -3032,7 +3043,7 @@
 
         err = mOMX->setParameter(
                 mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
-        CHECK_EQ(err, OK);
+        CHECK_EQ(err, (status_t)OK);
     }
 }
 
@@ -3044,10 +3055,10 @@
     OMX_INDEXTYPE index;
     status_t err = mOMX->get_extension_index(
             mNode, "OMX.TI.JPEG.decode.Config.OutputColorFormat", &index);
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     err = mOMX->set_config(mNode, index, &format, sizeof(format));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 #endif
 
     OMX_PARAM_PORTDEFINITIONTYPE def;
@@ -3056,13 +3067,13 @@
 
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
-    CHECK_EQ(def.eDomain, OMX_PortDomainImage);
+    CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainImage);
 
     OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
 
-    CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingUnused);
+    CHECK_EQ((int)imageDef->eCompressionFormat, (int)OMX_IMAGE_CodingUnused);
     imageDef->eColorFormat = format;
     imageDef->nFrameWidth = width;
     imageDef->nFrameHeight = height;
@@ -3105,7 +3116,7 @@
 
     err = mOMX->setParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 }
 
 void OMXCodec::setJPEGInputFormat(
@@ -3116,12 +3127,12 @@
 
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
-    CHECK_EQ(def.eDomain, OMX_PortDomainImage);
+    CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainImage);
     OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
 
-    CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingJPEG);
+    CHECK_EQ((int)imageDef->eCompressionFormat, (int)OMX_IMAGE_CodingJPEG);
     imageDef->nFrameWidth = width;
     imageDef->nFrameHeight = height;
 
@@ -3130,7 +3141,7 @@
 
     err = mOMX->setParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 }
 
 void OMXCodec::addCodecSpecificData(const void *data, size_t size) {
@@ -3231,7 +3242,7 @@
 
                 status_t err =
                     mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
             }
 
             while (mState != LOADED && mState != ERROR) {
@@ -3323,7 +3334,7 @@
 
         mFilledBuffers.clear();
 
-        CHECK_EQ(mState, EXECUTING);
+        CHECK_EQ((int)mState, (int)EXECUTING);
 
         bool emulateInputFlushCompletion = !flushPortAsync(kPortIndexInput);
         bool emulateOutputFlushCompletion = !flushPortAsync(kPortIndexOutput);
@@ -3377,7 +3388,7 @@
         BufferInfo *info = &buffers->editItemAt(i);
 
         if (info->mMediaBuffer == buffer) {
-            CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
+            CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)ENABLED);
             if (buffer->graphicBuffer() == 0) {
                 fillOutputBuffer(info);
             } else {
@@ -3629,7 +3640,7 @@
 
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     printf("%s Port = {\n", portIndex == kPortIndexInput ? "Input" : "Output");
 
@@ -3695,7 +3706,7 @@
 
                 err = mOMX->getParameter(
                         mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
 
                 printf("  nSamplingRate = %ld\n", params.nSamplingRate);
                 printf("  nChannels = %ld\n", params.nChannels);
@@ -3714,7 +3725,7 @@
 
                 err = mOMX->getParameter(
                         mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr));
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
 
                 printf("  nChannels = %ld\n", amr.nChannels);
                 printf("  eAMRBandMode = %s\n",
@@ -3764,13 +3775,14 @@
 
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 
     switch (def.eDomain) {
         case OMX_PortDomainImage:
         {
             OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
-            CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingUnused);
+            CHECK_EQ((int)imageDef->eCompressionFormat,
+                     (int)OMX_IMAGE_CodingUnused);
 
             mOutputFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
             mOutputFormat->setInt32(kKeyColorFormat, imageDef->eColorFormat);
@@ -3790,11 +3802,11 @@
 
                 err = mOMX->getParameter(
                         mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
 
-                CHECK_EQ(params.eNumData, OMX_NumericalDataSigned);
-                CHECK_EQ(params.nBitPerSample, 16);
-                CHECK_EQ(params.ePCMMode, OMX_AUDIO_PCMModeLinear);
+                CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned);
+                CHECK_EQ(params.nBitPerSample, 16u);
+                CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear);
 
                 int32_t numChannels, sampleRate;
                 inputFormat->findInt32(kKeyChannelCount, &numChannels);
@@ -3828,9 +3840,9 @@
 
                 err = mOMX->getParameter(
                         mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr));
-                CHECK_EQ(err, OK);
+                CHECK_EQ(err, (status_t)OK);
 
-                CHECK_EQ(amr.nChannels, 1);
+                CHECK_EQ(amr.nChannels, 1u);
                 mOutputFormat->setInt32(kKeyChannelCount, 1);
 
                 if (amr.eAMRBandMode >= OMX_AUDIO_AMRBandModeNB0
@@ -3885,6 +3897,37 @@
             mOutputFormat->setInt32(kKeyWidth, video_def->nFrameWidth);
             mOutputFormat->setInt32(kKeyHeight, video_def->nFrameHeight);
             mOutputFormat->setInt32(kKeyColorFormat, video_def->eColorFormat);
+
+            if (!mIsEncoder) {
+                OMX_CONFIG_RECTTYPE rect;
+                status_t err =
+                        mOMX->getConfig(
+                            mNode, OMX_IndexConfigCommonOutputCrop,
+                            &rect, sizeof(rect));
+
+                if (err == OK) {
+                    CHECK_GE(rect.nLeft, 0);
+                    CHECK_GE(rect.nTop, 0);
+                    CHECK_GE(rect.nWidth, 0u);
+                    CHECK_GE(rect.nHeight, 0u);
+                    CHECK_LE(rect.nLeft + rect.nWidth - 1, video_def->nFrameWidth);
+                    CHECK_LE(rect.nTop + rect.nHeight - 1, video_def->nFrameHeight);
+
+                    mOutputFormat->setRect(
+                            kKeyCropRect,
+                            rect.nLeft,
+                            rect.nTop,
+                            rect.nLeft + rect.nWidth - 1,
+                            rect.nTop + rect.nHeight - 1);
+                } else {
+                    mOutputFormat->setRect(
+                            kKeyCropRect,
+                            0, 0,
+                            video_def->nFrameWidth - 1,
+                            video_def->nFrameHeight - 1);
+                }
+            }
+
             break;
         }
 
@@ -3988,7 +4031,7 @@
             caps->mColorFormats.push(portFormat.eColorFormat);
         }
 
-        CHECK_EQ(omx->freeNode(node), OK);
+        CHECK_EQ(omx->freeNode(node), (status_t)OK);
     }
 }
 
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index e922c73..a9163fc 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -419,8 +419,10 @@
 
         ++left;
     }
+    if (left > 0) {
+        --left;
+    }
 
-    --left;
     uint32_t x;
     if (mDataSource->readAt(
                 mSyncSampleOffset + 8 + left * 4, &x, 4) != 4) {
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 9b2dec9..763a914 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -191,17 +191,26 @@
     CHECK(meta->findInt32(kKeyWidth, &width));
     CHECK(meta->findInt32(kKeyHeight, &height));
 
+    int32_t crop_left, crop_top, crop_right, crop_bottom;
+    if (!meta->findRect(
+                kKeyCropRect,
+                &crop_left, &crop_top, &crop_right, &crop_bottom)) {
+        crop_left = crop_top = 0;
+        crop_right = width - 1;
+        crop_bottom = height - 1;
+    }
+
     int32_t rotationAngle;
     if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
         rotationAngle = 0;  // By default, no rotation
     }
 
     VideoFrame *frame = new VideoFrame;
-    frame->mWidth = width;
-    frame->mHeight = height;
-    frame->mDisplayWidth = width;
-    frame->mDisplayHeight = height;
-    frame->mSize = width * height * 2;
+    frame->mWidth = crop_right - crop_left + 1;
+    frame->mHeight = crop_bottom - crop_top + 1;
+    frame->mDisplayWidth = frame->mWidth;
+    frame->mDisplayHeight = frame->mHeight;
+    frame->mSize = frame->mWidth * frame->mHeight * 2;
     frame->mData = new uint8_t[frame->mSize];
     frame->mRotationAngle = rotationAngle;
 
@@ -213,10 +222,13 @@
     CHECK(converter.isValid());
 
     converter.convert(
-            width, height,
             (const uint8_t *)buffer->data() + buffer->range_offset(),
-            0,
-            frame->mData, width * 2);
+            width, height,
+            crop_left, crop_top, crop_right, crop_bottom,
+            frame->mData,
+            frame->mWidth,
+            frame->mHeight,
+            0, 0, frame->mWidth - 1, frame->mHeight - 1);
 
     buffer->release();
     buffer = NULL;
@@ -418,5 +430,4 @@
     }
 }
 
-
 }  // namespace android
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 868c514..5bbba35 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -73,6 +73,7 @@
     CHECK(mSource->getFormat()->findInt32(kKeyHeight, &height));
     mFormat->setInt32(kKeyWidth, width);
     mFormat->setInt32(kKeyHeight, height);
+    mFormat->setRect(kKeyCropRect, 0, 0, width - 1, height - 1);
     mFormat->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar);
     mFormat->setCString(kKeyDecoderComponent, "AVCDecoder");
 
@@ -418,16 +419,32 @@
                     crop_top = crop_left = 0;
                 }
 
-                int32_t aligned_width = (crop_right - crop_left + 1 + 15) & ~15;
-                int32_t aligned_height = (crop_bottom - crop_top + 1 + 15) & ~15;
+                int32_t prevCropLeft, prevCropTop;
+                int32_t prevCropRight, prevCropBottom;
+                if (!mFormat->findRect(
+                            kKeyCropRect,
+                            &prevCropLeft, &prevCropTop,
+                            &prevCropRight, &prevCropBottom)) {
+                    prevCropLeft = prevCropTop = 0;
+                    prevCropRight = width - 1;
+                    prevCropBottom = height - 1;
+                }
 
                 int32_t oldWidth, oldHeight;
                 CHECK(mFormat->findInt32(kKeyWidth, &oldWidth));
                 CHECK(mFormat->findInt32(kKeyHeight, &oldHeight));
 
-                if (oldWidth != aligned_width || oldHeight != aligned_height) {
-                    mFormat->setInt32(kKeyWidth, aligned_width);
-                    mFormat->setInt32(kKeyHeight, aligned_height);
+                if (oldWidth != width || oldHeight != height
+                        || prevCropLeft != crop_left
+                        || prevCropTop != crop_top
+                        || prevCropRight != crop_right
+                        || prevCropBottom != crop_bottom) {
+                    mFormat->setRect(
+                            kKeyCropRect,
+                            crop_left, crop_top, crop_right, crop_bottom);
+
+                    mFormat->setInt32(kKeyWidth, width);
+                    mFormat->setInt32(kKeyHeight, height);
 
                     err = INFO_FORMAT_CHANGED;
                 } else {
diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk
index ef2dba0..62ba40f 100644
--- a/media/libstagefright/colorconversion/Android.mk
+++ b/media/libstagefright/colorconversion/Android.mk
@@ -9,20 +9,6 @@
         $(TOP)/frameworks/base/include/media/stagefright/openmax \
         $(TOP)/hardware/msm7k
 
-LOCAL_SHARED_LIBRARIES :=       \
-        libbinder               \
-        libmedia                \
-        libutils                \
-        libui                   \
-        libcutils				\
-        libsurfaceflinger_client\
-        libcamera_client
-
-# ifeq ($(TARGET_BOARD_PLATFORM),msm7k)
-ifeq ($(TARGET_PRODUCT),passion)
-	LOCAL_CFLAGS += -DHAS_YCBCR420_SP_ADRENO
-endif
-
 LOCAL_MODULE:= libstagefright_color_conversion
 
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 5b16997..600f040 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -50,31 +50,64 @@
     }
 }
 
-void ColorConverter::convert(
+ColorConverter::BitmapParams::BitmapParams(
+        void *bits,
         size_t width, size_t height,
-        const void *srcBits, size_t srcSkip,
-        void *dstBits, size_t dstSkip) {
+        size_t cropLeft, size_t cropTop,
+        size_t cropRight, size_t cropBottom)
+    : mBits(bits),
+      mWidth(width),
+      mHeight(height),
+      mCropLeft(cropLeft),
+      mCropTop(cropTop),
+      mCropRight(cropRight),
+      mCropBottom(cropBottom) {
+}
+
+size_t ColorConverter::BitmapParams::cropWidth() const {
+    return mCropRight - mCropLeft + 1;
+}
+
+size_t ColorConverter::BitmapParams::cropHeight() const {
+    return mCropBottom - mCropTop + 1;
+}
+
+void ColorConverter::convert(
+        const void *srcBits,
+        size_t srcWidth, size_t srcHeight,
+        size_t srcCropLeft, size_t srcCropTop,
+        size_t srcCropRight, size_t srcCropBottom,
+        void *dstBits,
+        size_t dstWidth, size_t dstHeight,
+        size_t dstCropLeft, size_t dstCropTop,
+        size_t dstCropRight, size_t dstCropBottom) {
     CHECK_EQ(mDstFormat, OMX_COLOR_Format16bitRGB565);
 
+    BitmapParams src(
+            const_cast<void *>(srcBits),
+            srcWidth, srcHeight,
+            srcCropLeft, srcCropTop, srcCropRight, srcCropBottom);
+
+    BitmapParams dst(
+            dstBits,
+            dstWidth, dstHeight,
+            dstCropLeft, dstCropTop, dstCropRight, dstCropBottom);
+
     switch (mSrcFormat) {
         case OMX_COLOR_FormatYUV420Planar:
-            convertYUV420Planar(
-                    width, height, srcBits, srcSkip, dstBits, dstSkip);
+            convertYUV420Planar(src, dst);
             break;
 
         case OMX_COLOR_FormatCbYCrY:
-            convertCbYCrY(
-                    width, height, srcBits, srcSkip, dstBits, dstSkip);
+            convertCbYCrY(src, dst);
             break;
 
         case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
-            convertQCOMYUV420SemiPlanar(
-                    width, height, srcBits, srcSkip, dstBits, dstSkip);
+            convertQCOMYUV420SemiPlanar(src, dst);
             break;
 
         case OMX_COLOR_FormatYUV420SemiPlanar:
-            convertYUV420SemiPlanar(
-                    width, height, srcBits, srcSkip, dstBits, dstSkip);
+            convertYUV420SemiPlanar(src, dst);
             break;
 
         default:
@@ -86,25 +119,27 @@
 }
 
 void ColorConverter::convertCbYCrY(
-        size_t width, size_t height,
-        const void *srcBits, size_t srcSkip,
-        void *dstBits, size_t dstSkip) {
-    CHECK_EQ(srcSkip, 0);  // Doesn't really make sense for YUV formats.
-    CHECK(dstSkip >= width * 2);
-    CHECK((dstSkip & 3) == 0);
+        const BitmapParams &src, const BitmapParams &dst) {
+    // XXX Untested
 
     uint8_t *kAdjustedClip = initClip();
 
-    uint32_t *dst_ptr = (uint32_t *)dstBits;
+    CHECK((src.mCropLeft & 1) == 0);
+    CHECK_EQ(src.cropWidth(), dst.cropWidth());
+    CHECK_EQ(src.cropHeight(), dst.cropHeight());
 
-    const uint8_t *src = (const uint8_t *)srcBits;
+    uint32_t *dst_ptr = (uint32_t *)dst.mBits
+        + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
 
-    for (size_t y = 0; y < height; ++y) {
-        for (size_t x = 0; x < width; x += 2) {
-            signed y1 = (signed)src[2 * x + 1] - 16;
-            signed y2 = (signed)src[2 * x + 3] - 16;
-            signed u = (signed)src[2 * x] - 128;
-            signed v = (signed)src[2 * x + 2] - 128;
+    const uint8_t *src_ptr = (const uint8_t *)src.mBits
+        + (src.mCropTop * dst.mWidth + src.mCropLeft) * 2;
+
+    for (size_t y = 0; y < src.cropHeight(); ++y) {
+        for (size_t x = 0; x < src.cropWidth(); x += 2) {
+            signed y1 = (signed)src_ptr[2 * x + 1] - 16;
+            signed y2 = (signed)src_ptr[2 * x + 3] - 16;
+            signed u = (signed)src_ptr[2 * x] - 128;
+            signed v = (signed)src_ptr[2 * x + 2] - 128;
 
             signed u_b = u * 517;
             signed u_g = -u * 100;
@@ -134,32 +169,35 @@
             dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
         }
 
-        src += width * 2;
-        dst_ptr += dstSkip / 4;
+        src_ptr += src.mWidth * 2;
+        dst_ptr += dst.mWidth / 2;
     }
 }
 
 void ColorConverter::convertYUV420Planar(
-        size_t width, size_t height,
-        const void *srcBits, size_t srcSkip,
-        void *dstBits, size_t dstSkip) {
-    CHECK_EQ(srcSkip, 0);  // Doesn't really make sense for YUV formats.
-    CHECK(dstSkip >= width * 2);
-    CHECK((dstSkip & 3) == 0);
-
+        const BitmapParams &src, const BitmapParams &dst) {
     uint8_t *kAdjustedClip = initClip();
 
-    uint32_t *dst_ptr = (uint32_t *)dstBits;
-    const uint8_t *src_y = (const uint8_t *)srcBits;
+    CHECK((dst.mWidth & 3) == 0);
+    CHECK((src.mCropLeft & 1) == 0);
+    CHECK_EQ(src.cropWidth(), dst.cropWidth());
+    CHECK_EQ(src.cropHeight(), dst.cropHeight());
+
+    uint32_t *dst_ptr = (uint32_t *)dst.mBits
+        + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+
+    const uint8_t *src_y =
+        (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
 
     const uint8_t *src_u =
-        (const uint8_t *)src_y + width * height;
+        (const uint8_t *)src_y + src.mWidth * src.mHeight
+        + src.mCropTop * (src.mWidth / 2) + src.mCropLeft / 2;
 
     const uint8_t *src_v =
-        (const uint8_t *)src_u + (width / 2) * (height / 2);
+        src_u + (src.mWidth / 2) * (src.mHeight / 2);
 
-    for (size_t y = 0; y < height; ++y) {
-        for (size_t x = 0; x < width; x += 2) {
+    for (size_t y = 0; y < src.cropHeight(); ++y) {
+        for (size_t x = 0; x < src.cropWidth(); x += 2) {
             // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
             // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
             // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
@@ -212,35 +250,38 @@
             dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
         }
 
-        src_y += width;
+        src_y += src.mWidth;
 
         if (y & 1) {
-            src_u += width / 2;
-            src_v += width / 2;
+            src_u += src.mWidth / 2;
+            src_v += src.mWidth / 2;
         }
 
-        dst_ptr += dstSkip / 4;
+        dst_ptr += dst.mWidth / 2;
     }
 }
 
 void ColorConverter::convertQCOMYUV420SemiPlanar(
-        size_t width, size_t height,
-        const void *srcBits, size_t srcSkip,
-        void *dstBits, size_t dstSkip) {
-    CHECK_EQ(srcSkip, 0);  // Doesn't really make sense for YUV formats.
-    CHECK(dstSkip >= width * 2);
-    CHECK((dstSkip & 3) == 0);
-
+        const BitmapParams &src, const BitmapParams &dst) {
     uint8_t *kAdjustedClip = initClip();
 
-    uint32_t *dst_ptr = (uint32_t *)dstBits;
-    const uint8_t *src_y = (const uint8_t *)srcBits;
+    CHECK((dst.mWidth & 3) == 0);
+    CHECK((src.mCropLeft & 1) == 0);
+    CHECK_EQ(src.cropWidth(), dst.cropWidth());
+    CHECK_EQ(src.cropHeight(), dst.cropHeight());
+
+    uint32_t *dst_ptr = (uint32_t *)dst.mBits
+        + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+
+    const uint8_t *src_y =
+        (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
 
     const uint8_t *src_u =
-        (const uint8_t *)src_y + width * height;
+        (const uint8_t *)src_y + src.mWidth * src.mHeight
+        + src.mCropTop * src.mWidth + src.mCropLeft;
 
-    for (size_t y = 0; y < height; ++y) {
-        for (size_t x = 0; x < width; x += 2) {
+    for (size_t y = 0; y < src.cropHeight(); ++y) {
+        for (size_t x = 0; x < src.cropWidth(); x += 2) {
             signed y1 = (signed)src_y[x] - 16;
             signed y2 = (signed)src_y[x + 1] - 16;
 
@@ -275,34 +316,39 @@
             dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
         }
 
-        src_y += width;
+        src_y += src.mWidth;
 
         if (y & 1) {
-            src_u += width;
+            src_u += src.mWidth;
         }
 
-        dst_ptr += dstSkip / 4;
+        dst_ptr += dst.mWidth / 2;
     }
 }
 
 void ColorConverter::convertYUV420SemiPlanar(
-        size_t width, size_t height,
-        const void *srcBits, size_t srcSkip,
-        void *dstBits, size_t dstSkip) {
-    CHECK_EQ(srcSkip, 0);  // Doesn't really make sense for YUV formats.
-    CHECK(dstSkip >= width * 2);
-    CHECK((dstSkip & 3) == 0);
+        const BitmapParams &src, const BitmapParams &dst) {
+    // XXX Untested
 
     uint8_t *kAdjustedClip = initClip();
 
-    uint32_t *dst_ptr = (uint32_t *)dstBits;
-    const uint8_t *src_y = (const uint8_t *)srcBits;
+    CHECK((dst.mWidth & 3) == 0);
+    CHECK((src.mCropLeft & 1) == 0);
+    CHECK_EQ(src.cropWidth(), dst.cropWidth());
+    CHECK_EQ(src.cropHeight(), dst.cropHeight());
+
+    uint32_t *dst_ptr = (uint32_t *)dst.mBits
+        + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+
+    const uint8_t *src_y =
+        (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
 
     const uint8_t *src_u =
-        (const uint8_t *)src_y + width * height;
+        (const uint8_t *)src_y + src.mWidth * src.mHeight
+        + src.mCropTop * src.mWidth + src.mCropLeft;
 
-    for (size_t y = 0; y < height; ++y) {
-        for (size_t x = 0; x < width; x += 2) {
+    for (size_t y = 0; y < src.cropHeight(); ++y) {
+        for (size_t x = 0; x < src.cropWidth(); x += 2) {
             signed y1 = (signed)src_y[x] - 16;
             signed y2 = (signed)src_y[x + 1] - 16;
 
@@ -337,13 +383,13 @@
             dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
         }
 
-        src_y += width;
+        src_y += src.mWidth;
 
         if (y & 1) {
-            src_u += width;
+            src_u += src.mWidth;
         }
 
-        dst_ptr += dstSkip / 4;
+        dst_ptr += dst.mWidth / 2;
     }
 }
 
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index acbea05..70408d7 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -21,55 +21,41 @@
 
 #include <binder/MemoryHeapBase.h>
 #include <binder/MemoryHeapPmem.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MetaData.h>
 #include <surfaceflinger/Surface.h>
 #include <ui/android_native_buffer.h>
 #include <ui/GraphicBufferMapper.h>
 
-// XXX: Temporary hack to allow referencing the _ADRENO pixel format here.
-#include <libgralloc-qsd8k/gralloc_priv.h>
-
 namespace android {
 
 SoftwareRenderer::SoftwareRenderer(
-        OMX_COLOR_FORMATTYPE colorFormat,
-        const sp<Surface> &surface,
-        size_t displayWidth, size_t displayHeight,
-        size_t decodedWidth, size_t decodedHeight,
-        int32_t rotationDegrees)
-    : mColorFormat(colorFormat),
-      mConverter(NULL),
+        const sp<Surface> &surface, const sp<MetaData> &meta)
+    : mConverter(NULL),
       mYUVMode(None),
-      mSurface(surface),
-      mDisplayWidth(displayWidth),
-      mDisplayHeight(displayHeight),
-      mDecodedWidth(decodedWidth),
-      mDecodedHeight(decodedHeight) {
-    LOGI("input format = %d", mColorFormat);
-    LOGI("display = %d x %d, decoded = %d x %d",
-            mDisplayWidth, mDisplayHeight, mDecodedWidth, mDecodedHeight);
+      mSurface(surface) {
+    int32_t tmp;
+    CHECK(meta->findInt32(kKeyColorFormat, &tmp));
+    mColorFormat = (OMX_COLOR_FORMATTYPE)tmp;
 
-    mDecodedWidth = mDisplayWidth;
-    mDecodedHeight = mDisplayHeight;
+    CHECK(meta->findInt32(kKeyWidth, &mWidth));
+    CHECK(meta->findInt32(kKeyHeight, &mHeight));
+
+    if (!meta->findRect(
+                kKeyCropRect,
+                &mCropLeft, &mCropTop, &mCropRight, &mCropBottom)) {
+        mCropLeft = mCropTop = 0;
+        mCropRight = mWidth - 1;
+        mCropBottom = mHeight - 1;
+    }
+
+    int32_t rotationDegrees;
+    if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
+        rotationDegrees = 0;
+    }
 
     int halFormat;
     switch (mColorFormat) {
-#if HAS_YCBCR420_SP_ADRENO
-        case OMX_COLOR_FormatYUV420Planar:
-        {
-            halFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO;
-            mYUVMode = YUV420ToYUV420sp;
-            break;
-        }
-
-        case 0x7fa30c00:
-        {
-            halFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO;
-            mYUVMode = YUV420spToYUV420sp;
-            break;
-        }
-#endif
-
         default:
             halFormat = HAL_PIXEL_FORMAT_RGB_565;
 
@@ -80,8 +66,8 @@
     }
 
     CHECK(mSurface.get() != NULL);
-    CHECK(mDecodedWidth > 0);
-    CHECK(mDecodedHeight > 0);
+    CHECK(mWidth > 0);
+    CHECK(mHeight > 0);
     CHECK(mConverter == NULL || mConverter->isValid());
 
     CHECK_EQ(0,
@@ -94,7 +80,9 @@
 
     // Width must be multiple of 32???
     CHECK_EQ(0, native_window_set_buffers_geometry(
-                mSurface.get(), mDecodedWidth, mDecodedHeight,
+                mSurface.get(),
+                mCropRight - mCropLeft + 1,
+                mCropBottom - mCropTop + 1,
                 halFormat));
 
     uint32_t transform;
@@ -117,10 +105,6 @@
     mConverter = NULL;
 }
 
-static inline size_t ALIGN(size_t x, size_t alignment) {
-    return (x + alignment - 1) & ~(alignment - 1);
-}
-
 void SoftwareRenderer::render(
         const void *data, size_t size, void *platformPrivate) {
     android_native_buffer_t *buf;
@@ -134,7 +118,7 @@
 
     GraphicBufferMapper &mapper = GraphicBufferMapper::get();
 
-    Rect bounds(mDecodedWidth, mDecodedHeight);
+    Rect bounds(mWidth, mHeight);
 
     void *dst;
     CHECK_EQ(0, mapper.lock(
@@ -142,69 +126,16 @@
 
     if (mConverter) {
         mConverter->convert(
-                mDecodedWidth, mDecodedHeight,
-                data, 0, dst, buf->stride * 2);
-    } else if (mYUVMode == YUV420spToYUV420sp) {
-        // Input and output are both YUV420sp, but the alignment requirements
-        // are different.
-        size_t srcYStride = mDecodedWidth;
-        const uint8_t *srcY = (const uint8_t *)data;
-        uint8_t *dstY = (uint8_t *)dst;
-        for (size_t i = 0; i < mDecodedHeight; ++i) {
-            memcpy(dstY, srcY, mDecodedWidth);
-            srcY += srcYStride;
-            dstY += buf->stride;
-        }
-
-        size_t srcUVStride = (mDecodedWidth + 1) & ~1;
-        size_t dstUVStride = ALIGN(mDecodedWidth / 2, 32) * 2;
-
-        const uint8_t *srcUV = (const uint8_t *)data
-            + mDecodedHeight * mDecodedWidth;
-
-        size_t dstUVOffset = ALIGN(ALIGN(mDecodedHeight, 32) * buf->stride, 4096);
-        uint8_t *dstUV = (uint8_t *)dst + dstUVOffset;
-
-        for (size_t i = 0; i < (mDecodedHeight + 1) / 2; ++i) {
-            memcpy(dstUV, srcUV, (mDecodedWidth + 1) & ~1);
-            srcUV += srcUVStride;
-            dstUV += dstUVStride;
-        }
-    } else if (mYUVMode == YUV420ToYUV420sp) {
-        // Input is YUV420 planar, output is YUV420sp, adhere to proper
-        // alignment requirements.
-        size_t srcYStride = mDecodedWidth;
-        const uint8_t *srcY = (const uint8_t *)data;
-        uint8_t *dstY = (uint8_t *)dst;
-        for (size_t i = 0; i < mDecodedHeight; ++i) {
-            memcpy(dstY, srcY, mDecodedWidth);
-            srcY += srcYStride;
-            dstY += buf->stride;
-        }
-
-        size_t srcUVStride = (mDecodedWidth + 1) / 2;
-        size_t dstUVStride = ALIGN(mDecodedWidth / 2, 32) * 2;
-
-        const uint8_t *srcU = (const uint8_t *)data
-            + mDecodedHeight * mDecodedWidth;
-
-        const uint8_t *srcV =
-            srcU + ((mDecodedWidth + 1) / 2) * ((mDecodedHeight + 1) / 2);
-
-        size_t dstUVOffset = ALIGN(ALIGN(mDecodedHeight, 32) * buf->stride, 4096);
-        uint8_t *dstUV = (uint8_t *)dst + dstUVOffset;
-
-        for (size_t i = 0; i < (mDecodedHeight + 1) / 2; ++i) {
-            for (size_t j = 0; j < (mDecodedWidth + 1) / 2; ++j) {
-                dstUV[2 * j + 1] = srcU[j];
-                dstUV[2 * j] = srcV[j];
-            }
-            srcU += srcUVStride;
-            srcV += srcUVStride;
-            dstUV += dstUVStride;
-        }
+                data,
+                mWidth, mHeight,
+                mCropLeft, mCropTop, mCropRight, mCropBottom,
+                dst,
+                buf->stride, buf->height,
+                0, 0,
+                mCropRight - mCropLeft,
+                mCropBottom - mCropTop);
     } else {
-        memcpy(dst, data, size);
+        TRESPASS();
     }
 
     CHECK_EQ(0, mapper.unlock(buf->handle));
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 4e63b7a..e33f467 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -88,11 +88,6 @@
 
     status_t seekTo(int64_t timeUs);
 
-    status_t getVideoDimensions(int32_t *width, int32_t *height) const;
-
-    status_t suspend();
-    status_t resume();
-
     // This is a mask of MediaExtractor::Flags.
     uint32_t flags() const;
 
@@ -153,7 +148,6 @@
     uint32_t mFlags;
     uint32_t mExtractorFlags;
 
-    int32_t mVideoWidth, mVideoHeight;
     int64_t mTimeSourceDeltaUs;
     int64_t mVideoTimeUs;
 
@@ -187,7 +181,6 @@
     void postCheckAudioStatusEvent_l();
     status_t play_l();
 
-    MediaBuffer *mLastVideoBuffer;
     MediaBuffer *mVideoBuffer;
 
     sp<NuHTTPDataSource> mConnectingDataSource;
@@ -198,32 +191,6 @@
     sp<ARTPSession> mRTPSession;
     sp<UDPPusher> mRTPPusher, mRTCPPusher;
 
-    struct SuspensionState {
-        String8 mUri;
-        KeyedVector<String8, String8> mUriHeaders;
-        sp<DataSource> mFileSource;
-
-        uint32_t mFlags;
-        int64_t mPositionUs;
-
-        void *mLastVideoFrame;
-        size_t mLastVideoFrameSize;
-        int32_t mColorFormat;
-        int32_t mVideoWidth, mVideoHeight;
-        int32_t mDecodedWidth, mDecodedHeight;
-
-        SuspensionState()
-            : mLastVideoFrame(NULL) {
-        }
-
-        ~SuspensionState() {
-            if (mLastVideoFrame) {
-                free(mLastVideoFrame);
-                mLastVideoFrame = NULL;
-            }
-        }
-    } *mSuspensionState;
-
     DrmManagerClient *mDrmManagerClient;
     DecryptHandle *mDecryptHandle;
 
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 9cafc68..90d3fe1 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -23,16 +23,13 @@
 
 namespace android {
 
+struct MetaData;
 class Surface;
 
 class SoftwareRenderer {
 public:
     SoftwareRenderer(
-            OMX_COLOR_FORMATTYPE colorFormat,
-            const sp<Surface> &surface,
-            size_t displayWidth, size_t displayHeight,
-            size_t decodedWidth, size_t decodedHeight,
-            int32_t rotationDegrees);
+            const sp<Surface> &surface, const sp<MetaData> &meta);
 
     ~SoftwareRenderer();
 
@@ -42,16 +39,14 @@
 private:
     enum YUVMode {
         None,
-        YUV420ToYUV420sp,
-        YUV420spToYUV420sp,
     };
 
     OMX_COLOR_FORMATTYPE mColorFormat;
     ColorConverter *mConverter;
     YUVMode mYUVMode;
     sp<Surface> mSurface;
-    size_t mDisplayWidth, mDisplayHeight;
-    size_t mDecodedWidth, mDecodedHeight;
+    int32_t mWidth, mHeight;
+    int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
 
     SoftwareRenderer(const SoftwareRenderer &);
     SoftwareRenderer &operator=(const SoftwareRenderer &);
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index ead1675..6e069c8 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -31,7 +31,6 @@
         libutils                \
         libui                   \
         libcutils               \
-        libstagefright_color_conversion
 
 ifneq ($(BUILD_WITHOUT_PV),true)
 LOCAL_SHARED_LIBRARIES += \
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index 7502f6e..b7e1a2a 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -22,7 +22,6 @@
 
 LOCAL_SRC_FILES:=                                       \
                   MtpClient.cpp                         \
-                  MtpCursor.cpp                         \
                   MtpDataPacket.cpp                     \
                   MtpDebug.cpp                          \
                   MtpDevice.cpp                         \
@@ -38,6 +37,7 @@
                   MtpStringBuffer.cpp                   \
                   MtpStorage.cpp                        \
                   MtpUtils.cpp                          \
+                  PtpCursor.cpp                         \
 
 LOCAL_MODULE:= libmtp
 
@@ -47,32 +47,3 @@
 
 endif
 
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=                                       \
-                  MtpClient.cpp                         \
-                  MtpCursor.cpp                         \
-                  MtpDataPacket.cpp                     \
-                  MtpDebug.cpp                          \
-                  MtpDevice.cpp                         \
-                  MtpEventPacket.cpp                    \
-                  MtpDeviceInfo.cpp                     \
-                  MtpObjectInfo.cpp                     \
-                  MtpPacket.cpp                         \
-                  MtpProperty.cpp                       \
-                  MtpRequestPacket.cpp                  \
-                  MtpResponsePacket.cpp                 \
-                  MtpStorageInfo.cpp                    \
-                  MtpStringBuffer.cpp                   \
-                  MtpStorage.cpp                        \
-                  MtpUtils.cpp                          \
-
-LOCAL_MODULE:= libmtp
-
-LOCAL_CFLAGS := -DMTP_HOST
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-endif
diff --git a/media/mtp/MtpPacket.cpp b/media/mtp/MtpPacket.cpp
index 42bf8ba..bd6196f 100644
--- a/media/mtp/MtpPacket.cpp
+++ b/media/mtp/MtpPacket.cpp
@@ -123,7 +123,7 @@
 
 uint32_t MtpPacket::getParameter(int index) const {
     if (index < 1 || index > 5) {
-        LOGE("index %d out of range in MtpRequestPacket::getParameter", index);
+        LOGE("index %d out of range in MtpPacket::getParameter", index);
         return 0;
     }
     return getUInt32(MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t));
@@ -131,7 +131,7 @@
 
 void MtpPacket::setParameter(int index, uint32_t value) {
     if (index < 1 || index > 5) {
-        LOGE("index %d out of range in MtpResponsePacket::setParameter", index);
+        LOGE("index %d out of range in MtpPacket::setParameter", index);
         return;
     }
     int offset = MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t);
diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp
index 86889c3..42945f5 100644
--- a/media/mtp/MtpProperty.cpp
+++ b/media/mtp/MtpProperty.cpp
@@ -312,6 +312,10 @@
     }
 }
 
+void MtpProperty::setFormDateTime() {
+     mFormFlag = kFormDateTime;
+}
+
 void MtpProperty::print() {
     LOGV("MtpProperty %04X\n", mCode);
     LOGV("    type %04X\n", mType);
diff --git a/media/mtp/MtpProperty.h b/media/mtp/MtpProperty.h
index c12399c..f783a87 100644
--- a/media/mtp/MtpProperty.h
+++ b/media/mtp/MtpProperty.h
@@ -58,6 +58,7 @@
         kFormNone = 0,
         kFormRange = 1,
         kFormEnum = 2,
+        kFormDateTime = 3,
     };
 
     uint32_t            mGroupCode;
@@ -90,6 +91,7 @@
 
     void                setFormRange(int min, int max, int step);
     void                setFormEnum(const int* values, int count);
+    void                setFormDateTime();
 
     void                print();
 
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index ca13636..c3755f3 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -66,7 +66,7 @@
 //    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
 //    MTP_OPERATION_MOVE_OBJECT,
 //    MTP_OPERATION_COPY_OBJECT,
-//    MTP_OPERATION_GET_PARTIAL_OBJECT,
+    MTP_OPERATION_GET_PARTIAL_OBJECT,
 //    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
     MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
     MTP_OPERATION_GET_OBJECT_PROP_DESC,
@@ -289,6 +289,9 @@
         case MTP_OPERATION_GET_OBJECT:
             response = doGetObject();
             break;
+        case MTP_OPERATION_GET_PARTIAL_OBJECT:
+            response = doGetPartialObject();
+            break;
         case MTP_OPERATION_SEND_OBJECT_INFO:
             response = doSendObjectInfo();
             break;
@@ -536,7 +539,7 @@
     MtpObjectFormat format = mRequest.getParameter(2);
     MtpDeviceProperty property = mRequest.getParameter(3);
     int groupCode = mRequest.getParameter(4);
-    int depth = mRequest.getParameter(4);
+    int depth = mRequest.getParameter(5);
    LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
             handle, MtpDebug::getFormatCodeName(format),
             MtpDebug::getObjectPropCodeName(property), groupCode, depth);
@@ -583,6 +586,45 @@
     return MTP_RESPONSE_OK;
 }
 
+MtpResponseCode MtpServer::doGetPartialObject() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    uint32_t offset = mRequest.getParameter(2);
+    uint32_t length = mRequest.getParameter(3);
+    MtpString pathBuf;
+    int64_t fileLength;
+    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
+    if (result != MTP_RESPONSE_OK)
+        return result;
+    if (offset + length > fileLength)
+        length = fileLength - offset;
+
+    const char* filePath = (const char *)pathBuf;
+    mtp_file_range  mfr;
+    mfr.fd = open(filePath, O_RDONLY);
+    if (mfr.fd < 0) {
+        return MTP_RESPONSE_GENERAL_ERROR;
+    }
+    mfr.offset = offset;
+    mfr.length = length;
+    mResponse.setParameter(1, length);
+
+    // send data header
+    mData.setOperationCode(mRequest.getOperationCode());
+    mData.setTransactionID(mRequest.getTransactionID());
+    mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE);
+
+    // then transfer the file
+    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
+    close(mfr.fd);
+    if (ret < 0) {
+        if (errno == ECANCELED)
+            return MTP_RESPONSE_TRANSACTION_CANCELLED;
+        else
+            return MTP_RESPONSE_GENERAL_ERROR;
+    }
+    return MTP_RESPONSE_OK;
+}
+
 MtpResponseCode MtpServer::doSendObjectInfo() {
     MtpString path;
     MtpStorageID storageID = mRequest.getParameter(1);
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index e65ddb0..5aee4ea 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -96,6 +96,7 @@
     MtpResponseCode     doGetObjectPropList();
     MtpResponseCode     doGetObjectInfo();
     MtpResponseCode     doGetObject();
+    MtpResponseCode     doGetPartialObject();
     MtpResponseCode     doSendObjectInfo();
     MtpResponseCode     doSendObject();
     MtpResponseCode     doDeleteObject();
diff --git a/media/mtp/MtpCursor.cpp b/media/mtp/PtpCursor.cpp
similarity index 91%
rename from media/mtp/MtpCursor.cpp
rename to media/mtp/PtpCursor.cpp
index 35d90dc..7294cef 100644
--- a/media/mtp/MtpCursor.cpp
+++ b/media/mtp/PtpCursor.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "MtpCursor"
+#define LOG_TAG "PtpCursor"
 
 #include "MtpDebug.h"
 #include "MtpClient.h"
-#include "MtpCursor.h"
+#include "PtpCursor.h"
 #include "MtpDevice.h"
 #include "MtpDeviceInfo.h"
 #include "MtpObjectInfo.h"
@@ -30,19 +30,19 @@
 namespace android {
 
 /* Device Column IDs */
-/* These must match the values in MtpCursor.java */
+/* These must match the values in PtpCursor.java */
 #define DEVICE_ROW_ID           1
 #define DEVICE_MANUFACTURER     2
 #define DEVICE_MODEL            3
 
 /* Storage Column IDs */
-/* These must match the values in MtpCursor.java */
+/* These must match the values in PtpCursor.java */
 #define STORAGE_ROW_ID          101
 #define STORAGE_IDENTIFIER      102
 #define STORAGE_DESCRIPTION     103
 
 /* Object Column IDs */
-/* These must match the values in MtpCursor.java */
+/* These must match the values in PtpCursor.java */
 #define OBJECT_ROW_ID               201
 #define OBJECT_STORAGE_ID           202
 #define OBJECT_FORMAT               203
@@ -65,7 +65,7 @@
 #define OBJECT_KEYWORDS             220
 #define OBJECT_THUMB                221
 
-MtpCursor::MtpCursor(MtpClient* client, int queryType, int deviceID,
+PtpCursor::PtpCursor(MtpClient* client, int queryType, int deviceID,
                 MtpStorageID storageID, MtpObjectHandle objectID,
                 int columnCount, int* columns)
         :   mClient(client),
@@ -82,12 +82,12 @@
     }
 }
 
-MtpCursor::~MtpCursor() {
+PtpCursor::~PtpCursor() {
     delete[] mColumns;
 }
 
-int MtpCursor::fillWindow(CursorWindow* window, int startPos) {
-    LOGD("MtpCursor::fillWindow mQueryType: %d\n", mQueryType);
+int PtpCursor::fillWindow(CursorWindow* window, int startPos) {
+    LOGD("PtpCursor::fillWindow mQueryType: %d\n", mQueryType);
 
     switch (mQueryType) {
         case DEVICE:
@@ -107,12 +107,12 @@
         case OBJECT_CHILDREN:
             return fillObjects(window, mQbjectID, startPos);
         default:
-            LOGE("MtpCursor::fillWindow: unknown query type %d\n", mQueryType);
+            LOGE("PtpCursor::fillWindow: unknown query type %d\n", mQueryType);
             return 0;
     }
 }
 
-int MtpCursor::fillDevices(CursorWindow* window, int startPos) {
+int PtpCursor::fillDevices(CursorWindow* window, int startPos) {
     int count = 0;
     MtpDeviceList& deviceList = mClient->getDeviceList();
     for (int i = 0; i < deviceList.size(); i++) {
@@ -127,7 +127,7 @@
     return count;
 }
 
-int MtpCursor::fillDevice(CursorWindow* window, int startPos) {
+int PtpCursor::fillDevice(CursorWindow* window, int startPos) {
     MtpDevice* device = mClient->getDevice(mDeviceID);
     if (device && fillDevice(window, device, startPos))
         return 1;
@@ -135,7 +135,7 @@
         return 0;
 }
 
-int MtpCursor::fillStorages(CursorWindow* window, int startPos) {
+int PtpCursor::fillStorages(CursorWindow* window, int startPos) {
     int count = 0;
     MtpDevice* device = mClient->getDevice(mDeviceID);
     if (!device)
@@ -157,7 +157,7 @@
     return count;
 }
 
-int MtpCursor::fillStorage(CursorWindow* window, int startPos) {
+int PtpCursor::fillStorage(CursorWindow* window, int startPos) {
     MtpDevice* device = mClient->getDevice(mDeviceID);
     if (device && fillStorage(window, device, mStorageID, startPos))
         return 1;
@@ -165,7 +165,7 @@
         return 0;
 }
 
-int MtpCursor::fillObjects(CursorWindow* window, int parent, int startPos) {
+int PtpCursor::fillObjects(CursorWindow* window, int parent, int startPos) {
     int count = 0;
     MtpDevice* device = mClient->getDevice(mDeviceID);
     if (!device)
@@ -187,7 +187,7 @@
     return count;
 }
 
-int MtpCursor::fillObject(CursorWindow* window, int startPos) {
+int PtpCursor::fillObject(CursorWindow* window, int startPos) {
     MtpDevice* device = mClient->getDevice(mDeviceID);
     if (device && fillObject(window, device, mQbjectID, startPos))
         return 1;
@@ -195,7 +195,7 @@
         return 0;
 }
 
-bool MtpCursor::fillDevice(CursorWindow* window, MtpDevice* device, int row) {
+bool PtpCursor::fillDevice(CursorWindow* window, MtpDevice* device, int row) {
     MtpDeviceInfo* deviceInfo = device->getDeviceInfo();
     if (!deviceInfo)
         return false;
@@ -225,7 +225,7 @@
     return true;
 }
 
-bool MtpCursor::fillStorage(CursorWindow* window, MtpDevice* device,
+bool PtpCursor::fillStorage(CursorWindow* window, MtpDevice* device,
         MtpStorageID storageID, int row) {
 
 LOGD("fillStorage %d\n", storageID);
@@ -273,7 +273,7 @@
     return false;
 }
 
-bool MtpCursor::fillObject(CursorWindow* window, MtpDevice* device,
+bool PtpCursor::fillObject(CursorWindow* window, MtpDevice* device,
         MtpObjectHandle objectID, int row) {
 
     MtpObjectInfo* objectInfo = device->getObjectInfo(objectID);
@@ -385,7 +385,7 @@
     return false;
 }
 
-bool MtpCursor::prepareRow(CursorWindow* window) {
+bool PtpCursor::prepareRow(CursorWindow* window) {
     if (!window->setNumColumns(mColumnCount)) {
         LOGE("Failed to change column count from %d to %d", window->getNumColumns(), mColumnCount);
         return false;
@@ -399,7 +399,7 @@
 }
 
 
-bool MtpCursor::putLong(CursorWindow* window, int64_t value, int row, int column) {
+bool PtpCursor::putLong(CursorWindow* window, int64_t value, int row, int column) {
     if (!window->putLong(row, column, value)) {
         window->freeLastRow();
         LOGE("Failed allocating space for a long in column %d", column);
@@ -408,7 +408,7 @@
     return true;
 }
 
-bool MtpCursor::putString(CursorWindow* window, const char* text, int row, int column) {
+bool PtpCursor::putString(CursorWindow* window, const char* text, int row, int column) {
     int size = strlen(text) + 1;
     int offset = window->alloc(size);
     if (!offset) {
@@ -427,7 +427,7 @@
     return true;
 }
 
-bool MtpCursor::putThumbnail(CursorWindow* window, MtpObjectHandle objectID,
+bool PtpCursor::putThumbnail(CursorWindow* window, MtpObjectHandle objectID,
                             MtpObjectFormat format, int row, int column) {
     MtpDevice* device = mClient->getDevice(mDeviceID);
     void* thumbnail;
diff --git a/media/mtp/MtpCursor.h b/media/mtp/PtpCursor.h
similarity index 93%
rename from media/mtp/MtpCursor.h
rename to media/mtp/PtpCursor.h
index 2e03c29..38a1d47 100644
--- a/media/mtp/MtpCursor.h
+++ b/media/mtp/PtpCursor.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _MTP_CURSOR_H
-#define _MTP_CURSOR_H
+#ifndef _PTP_CURSOR_H
+#define _PTP_CURSOR_H
 
 #include "MtpTypes.h"
 
@@ -23,7 +23,7 @@
 
 class CursorWindow;
 
-class MtpCursor {
+class PtpCursor {
 private:
     enum {
         DEVICE              = 1,
@@ -45,10 +45,10 @@
     int*            mColumns;
 
 public:
-                MtpCursor(MtpClient* client, int queryType, int deviceID,
+                PtpCursor(MtpClient* client, int queryType, int deviceID,
                         MtpStorageID storageID, MtpObjectHandle objectID,
                         int columnCount, int* columns);
-    virtual     ~MtpCursor();
+    virtual     ~PtpCursor();
 
     int         fillWindow(CursorWindow* window, int startPos);
 
@@ -75,4 +75,4 @@
 
 }; // namespace android
 
-#endif // _MTP_CURSOR_H
+#endif // _PTP_CURSOR_H
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java
index c04873a..0942d1f 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java
@@ -24,7 +24,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
-import android.provider.Mtp;
+import android.provider.Ptp;
 import android.util.Log;
 import android.view.View;
 import android.widget.ListAdapter;
@@ -58,7 +58,7 @@
     }
 
     private static final String[] DEVICE_COLUMNS =
-         new String[] { Mtp.Device._ID, Mtp.Device.MANUFACTURER, Mtp.Device.MODEL };
+         new String[] { Ptp.Device._ID, Ptp.Device.MANUFACTURER, Ptp.Device.MODEL };
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -71,7 +71,7 @@
     protected void onResume() {
         super.onResume();
 
-        Cursor c = getContentResolver().query(Mtp.Device.CONTENT_URI,
+        Cursor c = getContentResolver().query(Ptp.Device.CONTENT_URI,
                 DEVICE_COLUMNS, null, null, null);
         Log.d(TAG, "query returned " + c);
         startManagingCursor(c);
@@ -80,12 +80,12 @@
         // Map Cursor columns to views defined in simple_list_item_2.xml
         mAdapter = new SimpleCursorAdapter(this,
                 android.R.layout.simple_list_item_2, c,
-                        new String[] { Mtp.Device.MANUFACTURER, Mtp.Device.MODEL },
+                        new String[] { Ptp.Device.MANUFACTURER, Ptp.Device.MODEL },
                         new int[] { android.R.id.text1, android.R.id.text2 });
         setListAdapter(mAdapter);
 
         // register for changes to the device list
-        mResolver.registerContentObserver(Mtp.Device.CONTENT_URI, true, mDeviceObserver);
+        mResolver.registerContentObserver(Ptp.Device.CONTENT_URI, true, mDeviceObserver);
     }
 
     @Override
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
index 2060657..40c5978 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
@@ -25,7 +25,7 @@
 import android.media.MtpConstants;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.Mtp;
+import android.provider.Ptp;
 import android.util.Log;
 import android.view.View;
 import android.widget.AdapterView;
@@ -49,7 +49,7 @@
     private DeviceDisconnectedReceiver mDisconnectedReceiver;
 
     private static final String[] OBJECT_COLUMNS =
-        new String[] { Mtp.Object._ID, Mtp.Object.NAME, Mtp.Object.FORMAT, Mtp.Object.THUMB };
+        new String[] { Ptp.Object._ID, Ptp.Object.NAME, Ptp.Object.FORMAT, Ptp.Object.THUMB };
 
     static final int ID_COLUMN = 0;
     static final int NAME_COLUMN = 1;
@@ -74,9 +74,9 @@
             Cursor c;
             Uri uri;
             if (mObjectID == 0) {
-                uri = Mtp.Object.getContentUriForStorageChildren(mDeviceID, mStorageID);
+                uri = Ptp.Object.getContentUriForStorageChildren(mDeviceID, mStorageID);
             } else {
-                uri = Mtp.Object.getContentUriForObjectChildren(mDeviceID, mObjectID);
+                uri = Ptp.Object.getContentUriForObjectChildren(mDeviceID, mObjectID);
             }
             Log.d(TAG, "query " + uri);
             c = getContentResolver().query(uri, OBJECT_COLUMNS, null, null, null);
@@ -99,7 +99,7 @@
     protected void onListItemClick(ListView l, View v, int position, long id) {
         long rowID = mAdapter.getItemId(position);
         Cursor c = getContentResolver().query(
-                        Mtp.Object.getContentUri(mDeviceID, rowID),
+                        Ptp.Object.getContentUri(mDeviceID, rowID),
                         OBJECT_COLUMNS, null, null, null);
         Log.d(TAG, "query returned " + c + " count: " + c.getCount());
         long format = 0;
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
index 4e63128..3a6c6a4 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
@@ -24,7 +24,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
-import android.provider.Mtp;
+import android.provider.Ptp;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
@@ -51,21 +51,21 @@
     private DeviceDisconnectedReceiver mDisconnectedReceiver;
 
     private static final String[] OBJECT_COLUMNS =
-        new String[] {  Mtp.Object._ID,
-                        Mtp.Object.NAME,
-                        Mtp.Object.SIZE,
-                        Mtp.Object.THUMB_WIDTH,
-                        Mtp.Object.THUMB_HEIGHT,
-                        Mtp.Object.THUMB_SIZE,
-                        Mtp.Object.IMAGE_WIDTH,
-                        Mtp.Object.IMAGE_HEIGHT,
-                        Mtp.Object.IMAGE_DEPTH,
-                        Mtp.Object.SEQUENCE_NUMBER,
-                        Mtp.Object.DATE_CREATED,
-                        Mtp.Object.DATE_MODIFIED,
-                        Mtp.Object.KEYWORDS,
-                        Mtp.Object.THUMB,
-                        Mtp.Object.FORMAT,
+        new String[] {  Ptp.Object._ID,
+                        Ptp.Object.NAME,
+                        Ptp.Object.SIZE,
+                        Ptp.Object.THUMB_WIDTH,
+                        Ptp.Object.THUMB_HEIGHT,
+                        Ptp.Object.THUMB_SIZE,
+                        Ptp.Object.IMAGE_WIDTH,
+                        Ptp.Object.IMAGE_HEIGHT,
+                        Ptp.Object.IMAGE_DEPTH,
+                        Ptp.Object.SEQUENCE_NUMBER,
+                        Ptp.Object.DATE_CREATED,
+                        Ptp.Object.DATE_MODIFIED,
+                        Ptp.Object.KEYWORDS,
+                        Ptp.Object.THUMB,
+                        Ptp.Object.FORMAT,
                         };
 
     @Override
@@ -91,7 +91,7 @@
 
         if (mDeviceID != 0 && mObjectID != 0) {
         Cursor c = getContentResolver().query(
-                        Mtp.Object.getContentUri(mDeviceID, mObjectID),
+                        Ptp.Object.getContentUri(mDeviceID, mObjectID),
                         OBJECT_COLUMNS, null, null, null);
             c.moveToFirst();
             TextView view = (TextView)findViewById(R.id.name);
@@ -147,7 +147,7 @@
         dest.mkdirs();
         dest = new File(dest, mFileName);
 
-        Uri requestUri = Mtp.Object.getContentUriForImport(mDeviceID, mObjectID,
+        Uri requestUri = Ptp.Object.getContentUriForImport(mDeviceID, mObjectID,
                 dest.getAbsolutePath());
         Uri resultUri = getContentResolver().insert(requestUri, new ContentValues());
         Log.d(TAG, "save returned " + resultUri);
@@ -162,7 +162,7 @@
     }
 
     private void deleteObject() {
-        Uri uri = Mtp.Object.getContentUri(mDeviceID, mObjectID);
+        Uri uri = Ptp.Object.getContentUri(mDeviceID, mObjectID);
 
         Log.d(TAG, "deleting " + uri);
 
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
index 4da88d6..62187b0 100644
--- a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
@@ -21,7 +21,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.Mtp;
+import android.provider.Ptp;
 import android.util.Log;
 import android.view.View;
 import android.widget.ListAdapter;
@@ -40,7 +40,7 @@
     private DeviceDisconnectedReceiver mDisconnectedReceiver;
 
     private static final String[] STORAGE_COLUMNS =
-        new String[] { Mtp.Storage._ID, Mtp.Storage.DESCRIPTION };
+        new String[] { Ptp.Storage._ID, Ptp.Storage.DESCRIPTION };
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -54,7 +54,7 @@
         super.onResume();
 
         if (mDeviceID != 0) {
-            Cursor c = getContentResolver().query(Mtp.Storage.getContentUri(mDeviceID),
+            Cursor c = getContentResolver().query(Ptp.Storage.getContentUri(mDeviceID),
                     STORAGE_COLUMNS, null, null, null);
             Log.d(TAG, "query returned " + c);
             startManagingCursor(c);
@@ -62,7 +62,7 @@
             // Map Cursor columns to views defined in simple_list_item_1.xml
             mAdapter = new SimpleCursorAdapter(this,
                     android.R.layout.simple_list_item_1, c,
-                            new String[] { Mtp.Storage.DESCRIPTION },
+                            new String[] { Ptp.Storage.DESCRIPTION },
                             new int[] { android.R.id.text1, android.R.id.text2 });
             setListAdapter(mAdapter);
         }
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
deleted file mode 100644
index 6baf5ea..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png
new file mode 100644
index 0000000..615c8b6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png
new file mode 100644
index 0000000..0122025
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png
new file mode 100644
index 0000000..0786916
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png
new file mode 100644
index 0000000..35f9240
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.png
deleted file mode 100644
index 9ababb7..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
deleted file mode 100644
index ed813dc..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png
new file mode 100644
index 0000000..3eb22df
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png
new file mode 100644
index 0000000..1ce9bd1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png
index bf33c946..259db7a 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_pressed.png
new file mode 100644
index 0000000..993ea55
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
deleted file mode 100644
index d7775f2..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png
new file mode 100644
index 0000000..fef2cf9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png
new file mode 100644
index 0000000..05593bc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
deleted file mode 100644
index 178af73..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png
new file mode 100644
index 0000000..32c2c79
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png
new file mode 100644
index 0000000..142c413
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.png
deleted file mode 100644
index ffbd2d3..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
index e81cd7c..9216030 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png
index e81cd7c..9216030 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
index c56ae8a..e529f6f 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png
index bf720cbb..e529f6f 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.png
new file mode 100644
index 0000000..02c27ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
index 38e3b24..57558ad 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png
index 3559497..57558ad 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
index 6f98e72..e4425b2 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png
index 8b932a6..e4425b2 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.png
new file mode 100644
index 0000000..84ac927
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
index 759631c..09de6b0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png
index 45c4509..09de6b0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.png
new file mode 100644
index 0000000..13cae40
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
index da5b02c..690b5f6c7 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.png
new file mode 100644
index 0000000..d0a4fd0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.png
new file mode 100644
index 0000000..05976bd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.png
new file mode 100644
index 0000000..2cc3cd6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
index 2989939..1c59b2a 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0_fully.png
new file mode 100644
index 0000000..1c59b2a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
index 6f40cb4..32e9165 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png
index 60e3794a..32e9165 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
index ab1b0c67..ea71298 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png
index eb25919..ea71298 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
index b574115..869a497 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png
index 14cd8fa..869a497 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
index 3b81ceb..1711c82 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png
index ef72980..1711c82 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_home.xml b/packages/SystemUI/res/drawable/ic_sysbar_back.xml
similarity index 89%
copy from packages/SystemUI/res/drawable/status_bar_home.xml
copy to packages/SystemUI/res/drawable/ic_sysbar_back.xml
index 0011711..327ccd8 100644
--- a/packages/SystemUI/res/drawable/status_bar_home.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_back.xml
@@ -15,7 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/status_bar_home_pressed" />
-    <item android:drawable="@drawable/status_bar_home_default" />
+    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_back_pressed" />
+    <item android:drawable="@drawable/ic_sysbar_back_default" />
 </selector>
 
diff --git a/packages/SystemUI/res/drawable/status_bar_home.xml b/packages/SystemUI/res/drawable/ic_sysbar_home.xml
similarity index 89%
rename from packages/SystemUI/res/drawable/status_bar_home.xml
rename to packages/SystemUI/res/drawable/ic_sysbar_home.xml
index 0011711..f4e585e 100644
--- a/packages/SystemUI/res/drawable/status_bar_home.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_home.xml
@@ -15,7 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/status_bar_home_pressed" />
-    <item android:drawable="@drawable/status_bar_home_default" />
+    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_home_pressed" />
+    <item android:drawable="@drawable/ic_sysbar_home_default" />
 </selector>
 
diff --git a/packages/SystemUI/res/drawable/status_bar_home.xml b/packages/SystemUI/res/drawable/ic_sysbar_ime.xml
similarity index 89%
copy from packages/SystemUI/res/drawable/status_bar_home.xml
copy to packages/SystemUI/res/drawable/ic_sysbar_ime.xml
index 0011711..1accf00 100644
--- a/packages/SystemUI/res/drawable/status_bar_home.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_ime.xml
@@ -15,7 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/status_bar_home_pressed" />
-    <item android:drawable="@drawable/status_bar_home_default" />
+    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_ime_pressed" />
+    <item android:drawable="@drawable/ic_sysbar_ime_default" />
 </selector>
 
diff --git a/packages/SystemUI/res/drawable/status_bar_home.xml b/packages/SystemUI/res/drawable/ic_sysbar_menu.xml
similarity index 89%
copy from packages/SystemUI/res/drawable/status_bar_home.xml
copy to packages/SystemUI/res/drawable/ic_sysbar_menu.xml
index 0011711..7a10607 100644
--- a/packages/SystemUI/res/drawable/status_bar_home.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_menu.xml
@@ -15,7 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/status_bar_home_pressed" />
-    <item android:drawable="@drawable/status_bar_home_default" />
+    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_menu_pressed" />
+    <item android:drawable="@drawable/ic_sysbar_menu_default" />
 </selector>
 
diff --git a/packages/SystemUI/res/drawable/status_bar_home.xml b/packages/SystemUI/res/drawable/ic_sysbar_recent.xml
old mode 100644
new mode 100755
similarity index 88%
copy from packages/SystemUI/res/drawable/status_bar_home.xml
copy to packages/SystemUI/res/drawable/ic_sysbar_recent.xml
index 0011711..39a324b
--- a/packages/SystemUI/res/drawable/status_bar_home.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_recent.xml
@@ -15,7 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/status_bar_home_pressed" />
-    <item android:drawable="@drawable/status_bar_home_default" />
+    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_recent_pressed" />
+    <item android:drawable="@drawable/ic_sysbar_recent_default" />
 </selector>
 
diff --git a/packages/SystemUI/res/drawable/status_bar_back.xml b/packages/SystemUI/res/drawable/status_bar_back.xml
deleted file mode 100644
index 92bf147..0000000
--- a/packages/SystemUI/res/drawable/status_bar_back.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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_pressed="true" android:drawable="@drawable/status_bar_back_pressed" />
-    <item android:drawable="@drawable/status_bar_back_default" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/status_bar_menu.xml b/packages/SystemUI/res/drawable/status_bar_menu.xml
deleted file mode 100644
index aa7286e..0000000
--- a/packages/SystemUI/res/drawable/status_bar_menu.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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_pressed="true" android:drawable="@drawable/status_bar_menu_pressed" />
-    <item android:drawable="@drawable/status_bar_menu_default" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/status_bar_recent.xml b/packages/SystemUI/res/drawable/status_bar_recent.xml
deleted file mode 100755
index d708455..0000000
--- a/packages/SystemUI/res/drawable/status_bar_recent.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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_pressed="true" android:drawable="@drawable/status_bar_recent_pressed" />
-    <item android:drawable="@drawable/status_bar_recent_default" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_icon_bg.xml b/packages/SystemUI/res/drawable/status_bar_ticker_background.xml
similarity index 73%
rename from packages/SystemUI/res/drawable/ic_sysbar_icon_bg.xml
rename to packages/SystemUI/res/drawable/status_bar_ticker_background.xml
index d8ba2a8..c230358 100644
--- a/packages/SystemUI/res/drawable/ic_sysbar_icon_bg.xml
+++ b/packages/SystemUI/res/drawable/status_bar_ticker_background.xml
@@ -4,9 +4,9 @@
      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.
@@ -14,8 +14,14 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_press_bg" />
-    <item android:drawable="@drawable/ic_sysbar_default_bg" />
-</selector>
+<layer-list
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:opacity="translucent"
+    >
+    <item
+        android:drawable="@drawable/ticker_background_color"
+        android:top="12dp"
+        />
+</layer-list>
+
 
diff --git a/packages/SystemUI/res/drawable/status_bar_home.xml b/packages/SystemUI/res/drawable/ticker_background.xml
similarity index 61%
copy from packages/SystemUI/res/drawable/status_bar_home.xml
copy to packages/SystemUI/res/drawable/ticker_background.xml
index 0011711..7320fa0 100644
--- a/packages/SystemUI/res/drawable/status_bar_home.xml
+++ b/packages/SystemUI/res/drawable/ticker_background.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
+<!-- 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.
@@ -14,8 +14,14 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/status_bar_home_pressed" />
-    <item android:drawable="@drawable/status_bar_home_default" />
-</selector>
+<layer-list
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:opacity="translucent"
+    >
+    <!-- the large icon extends 12dp beyond the edge of the status bar -->
+    <item
+        android:drawable="@drawable/notification_item_background_color"
+        android:top="12dp"
+        />
+</layer-list>
 
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index d11e6da..488dbba 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -39,7 +39,6 @@
                 android:id="@+id/notificationIcons"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:gravity="center_vertical"
                 android:orientation="horizontal"
                 >
                 <view
@@ -47,7 +46,9 @@
                     android:id="@+id/icons"
                     android:layout_width="wrap_content"
                     android:layout_height="@*android:dimen/status_bar_icon_size"
-                    android:layout_marginLeft="8dip"
+                    android:layout_gravity="top"
+                    android:layout_marginTop="5dp"
+                    android:layout_marginLeft="8dp"
                     />
             </com.android.systemui.statusbar.tablet.NotificationIconArea>
 
@@ -79,19 +80,24 @@
                     android:layout_width="48dip"
                     android:layout_height="match_parent"
                     android:orientation="horizontal"
+                    android:gravity="center"
                     >
                     <ImageView
-                        android:id="@+id/battery"
-                        android:layout_height="wrap_content"
-                        android:layout_width="wrap_content"
-                        android:layout_gravity="center_vertical"
-                        />
-                    <ImageView
                         android:id="@+id/network"
                         android:layout_height="wrap_content"
                         android:layout_width="wrap_content"
-                        android:layout_gravity="center_vertical"
-                        android:src="@drawable/ic_sysbar_wifi_mini"
+                        android:layout_gravity="top"
+                        android:layout_marginTop="19dp"
+                        android:layout_marginRight="4dp"
+                        />
+                    <ImageView
+                        android:id="@+id/battery"
+                        android:layout_height="wrap_content"
+                        android:layout_width="wrap_content"
+                        android:layout_gravity="top"
+                        android:layout_marginTop="19dp"
+                        android:layout_marginLeft="2dp"
+                        android:layout_marginRight="2dp"
                         />
                 </LinearLayout>
             </LinearLayout>
@@ -112,7 +118,6 @@
                 android:paddingLeft="18dip"
                 android:paddingRight="18dip"
                 android:src="@drawable/ic_sysbar_back"
-                android:background="@drawable/ic_sysbar_icon_bg"
                 systemui:keyCode="4"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
@@ -121,14 +126,13 @@
                 android:paddingLeft="18dip"
                 android:paddingRight="18dip"
                 android:src="@drawable/ic_sysbar_home"
-                android:background="@drawable/ic_sysbar_icon_bg"
                 systemui:keyCode="3"
                 />
             <ImageButton android:id="@+id/recent_apps"
                 android:layout_width="96dip"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
-                android:background="@drawable/ic_sysbar_icon_bg"
+                android:background="@null"
                 android:paddingLeft="18dip"
                 android:clickable="true"
                 android:paddingRight="18dip"
@@ -139,7 +143,6 @@
                 android:paddingLeft="18dip"
                 android:paddingRight="18dip"
                 android:src="@drawable/ic_sysbar_menu"
-                android:background="@drawable/ic_sysbar_icon_bg"
                 systemui:keyCode="82"
                 android:visibility="invisible"
                 />
@@ -147,7 +150,6 @@
                 android:id="@+id/pocket"
                 android:layout_width="96dip"
                 android:layout_height="match_parent"
-                android:background="@drawable/ic_sysbar_icon_bg"
                 android:paddingLeft="18dip"
                 android:paddingRight="18dip"
                 android:animateLayoutChanges="true"
@@ -169,8 +171,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
                 android:layout_marginLeft="8dip"
-                android:src="@drawable/ic_sysbar_ime_default"
-                android:background="@drawable/ic_sysbar_icon_bg"
+                android:src="@drawable/ic_sysbar_ime"
                 android:visibility="invisible"
                 />
         </LinearLayout>
@@ -238,17 +239,5 @@
                 android:visibility="gone"
                 />
         </RelativeLayout>
-
-        <!-- ticker: transient incoming notification information -->
-        <FrameLayout
-            android:id="@+id/ticker"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_alignParentRight="true"
-            android:layout_toRightOf="@+id/systemInfo"
-            android:paddingLeft="6dip"
-            android:gravity="center_vertical"
-            />
-            
     </RelativeLayout>
 </com.android.systemui.statusbar.tablet.TabletStatusBarView>
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml b/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml
index b6679a5..6b12d29 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml
@@ -13,13 +13,24 @@
         android:src="@drawable/status_bar_veto"
         android:scaleType="center"
         android:background="#ff000000"
+        android:paddingRight="8dp"
         />
 
+    <ImageView
+        android:id="@+id/large_icon"
+        android:layout_width="@dimen/notification_large_icon_width"
+        android:layout_height="@dimen/notification_large_icon_height"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true"
+        android:scaleType="center"
+        />
+        <!-- TODO: scaleType should be top-left but ImageView doesn't support that. -->
+
     <com.android.systemui.statusbar.LatestItemView android:id="@+id/content"
         android:layout_width="wrap_content"
         android:layout_height="64sp"
         android:layout_alignParentTop="true"
-        android:layout_alignParentLeft="true"
+        android:layout_toRightOf="@id/large_icon"
         android:layout_toLeftOf="@id/veto"
         android:layout_marginLeft="16dp"
         android:focusable="true"
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
index b23cc7b..5fa8b3b 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
@@ -1,43 +1,40 @@
-<?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.
-*/
+  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.
 -->
 
 <!--    android:background="@drawable/status_bar_closed_default_background" -->
 <com.android.systemui.statusbar.tablet.NotificationPanel
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="match_parent"
-    android:layout_width="540dp"
+    android:layout_width="match_parent"
     android:animateLayoutChanges="true"
     android:background="@drawable/bg_scrim_notification"
+    android:paddingTop="32dp"
     android:paddingBottom="32dp"
+    android:orientation="vertical"
+    android:gravity="right"
     >
 
     <com.android.systemui.statusbar.tablet.NotificationTitleArea
         android:id="@+id/title_area"
-        android:layout_height="wrap_content"
+        android:layout_height="160dp"
         android:layout_width="384dp"
-        android:layout_above="@+id/content_frame"
         android:layout_marginLeft="24dp"
-        android:paddingBottom="16dp"
+        android:paddingTop="20dp"
         android:orientation="vertical"
         android:animateLayoutChanges="true"
-        android:layout_alignParentRight="true"
         >
 
         <com.android.systemui.statusbar.tablet.HoloClock
@@ -81,7 +78,6 @@
             android:layout_below="@id/date"
             android:layout_marginTop="16dp"
             android:layout_marginLeft="48dp"
-            android:src="@drawable/ic_sysbar_battery_mini"
             android:baseline="17dp"
             />
 
@@ -101,7 +97,6 @@
             android:layout_width="wrap_content"
             android:layout_toRightOf="@id/battery_text"
             android:layout_alignBaseline="@id/battery"
-            android:src="@drawable/ic_sysbar_wifi_mini"
             android:baseline="21dp"
             />
 
@@ -143,8 +138,6 @@
         android:id="@+id/content_frame"
         android:layout_height="wrap_content"
         android:layout_width="408dp"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentRight="true"
         >
         <ScrollView
             android:id="@+id/notificationScroller"
diff --git a/packages/SystemUI/res/layout-xlarge/ticker.xml b/packages/SystemUI/res/layout-xlarge/ticker.xml
index c8d855f..cae6a77 100644
--- a/packages/SystemUI/res/layout-xlarge/ticker.xml
+++ b/packages/SystemUI/res/layout-xlarge/ticker.xml
@@ -18,36 +18,22 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_height="wrap_content"
     android:orientation="horizontal"
-    android:background="#ff000000"
+    android:background="@drawable/status_bar_ticker_background"
+    android:gravity="bottom"
     >
+    <!--
+    android:background="@drawable/ticker_background"
+    -->
 
-    <LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:layout_gravity="center_vertical"
-        android:orientation="vertical"
-        android:paddingLeft="12dp"
-        >
-
-        <TextView android:id="@+id/title"
-            xmlns:android="http://schemas.android.com/apk/res/android"
-            android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textStyle="bold"
-            android:maxLines="1"
-            />
-        <TextView android:id="@+id/subtitle"
-            xmlns:android="http://schemas.android.com/apk/res/android"
-            android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:maxLines="1"
-            />
-    </LinearLayout>
+    <ImageView
+        android:id="@+id/large_icon"
+        android:layout_width="@dimen/notification_large_icon_height"
+        android:layout_height="@dimen/notification_large_icon_width"
+        android:scaleType="center"
+        android:visibility="gone"
+        />
+        <!-- TODO: scaleType should be top-left but ImageView doesn't support that. -->
 
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/ticker_compat.xml b/packages/SystemUI/res/layout-xlarge/ticker_compat.xml
index 79c7543..5d9a680 100644
--- a/packages/SystemUI/res/layout-xlarge/ticker_compat.xml
+++ b/packages/SystemUI/res/layout-xlarge/ticker_compat.xml
@@ -18,11 +18,26 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_height="@*android:dimen/status_bar_height"
     android:orientation="horizontal"
-    android:background="#ff000000"
+    android:background="@drawable/status_bar_ticker_background"
     >
 
+    <ImageView
+        android:id="@+id/large_icon"
+        android:layout_width="@dimen/notification_large_icon_width"
+        android:layout_height="@dimen/notification_large_icon_height"
+        android:scaleType="center"
+        android:visibility="gone"
+        />
+    
+    <ImageView android:id="@+id/left_icon"
+        android:layout_width="64dp"
+        android:layout_height="match_parent"
+        android:scaleType="center"
+        android:visibility="gone"
+        />
+
     <TextView android:id="@+id/text"
         android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
         android:layout_width="match_parent"
@@ -33,4 +48,11 @@
         android:maxLines="2"
         />
 
+    <ImageView android:id="@+id/right_icon"
+        android:layout_width="64dp"
+        android:layout_height="match_parent"
+        android:scaleType="center"
+        android:visibility="gone"
+        />
+
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 3ad199e..18e8273 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -71,6 +71,7 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:fadingEdge="none"
+            android:overScrollMode="ifContentScrolls"
             >
             <LinearLayout
                 android:id="@+id/notificationLinearLayout"
diff --git a/packages/SystemUI/res/values-xlarge/config.xml b/packages/SystemUI/res/values-xlarge/config.xml
index e6af4f5..e140914 100644
--- a/packages/SystemUI/res/values-xlarge/config.xml
+++ b/packages/SystemUI/res/values-xlarge/config.xml
@@ -26,5 +26,8 @@
      interface.  This name is in the ComponentName flattened format (package/class)  -->
     <string name="config_statusBarComponent">com.android.systemui.statusbar.tablet.TabletStatusBar</string>
 
+    <!-- Whether or not we show the number in the bar. -->
+    <bool name="config_statusBarShowNumber">false</bool>
+
 </resources>
 
diff --git a/packages/SystemUI/res/values-xlarge/dimens.xml b/packages/SystemUI/res/values-xlarge/dimens.xml
new file mode 100644
index 0000000..009b7a8
--- /dev/null
+++ b/packages/SystemUI/res/values-xlarge/dimens.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>
+    <!-- The width of the big icons in notifications. -->
+    <dimen name="notification_large_icon_width">60dp</dimen>
+    <!-- The width of the big icons in notifications. -->
+    <dimen name="notification_large_icon_height">60dp</dimen>
+    <!-- The width of the ticker, including the icon -->
+    <dimen name="notification_ticker_width">360dp</dimen>
+</resources>
+
+
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index fd2cf99..964e69b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -19,4 +19,5 @@
 <resources>
     <drawable name="notification_number_text_color">#ffffffff</drawable>
     <drawable name="notification_item_background_color">#ff000000</drawable>
+    <drawable name="ticker_background_color">#ff1d1d1d</drawable>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 86beb14..05ed089 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -35,5 +35,8 @@
      interface.  This name is in the ComponentName flattened format (package/class)  -->
     <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>
 
+    <!-- Whether or not we show the number in the bar. -->
+    <bool name="config_statusBarShowNumber">true</bool>
+
 </resources>
 
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
index ff2a4ed..a98ef0b 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -193,14 +193,13 @@
                 ActivityDescription item = mActivityDescriptions.get(n);
                 if (item.id >= 0) {
                     // This is an active task; it should just go to the foreground.
-                    IActivityManager am = ActivityManagerNative.getDefault();
-                    try {
-                        am.moveTaskToFront(item.id);
-                    } catch (RemoteException e) {
-                    }
+                    final ActivityManager am = (ActivityManager)
+                            getSystemService(Context.ACTIVITY_SERVICE);
+                    am.moveTaskToFront(item.id, ActivityManager.MOVE_TASK_WITH_HOME);
                 } else if (item.intent != null) {
                     // prepare a launch intent and send it
-                    item.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+                    item.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+                            | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
                     try {
                         if (DBG) Log.v(TAG, "Starting intent " + item.intent);
                         startActivity(item.intent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 9a61be6..dbfbe11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -93,7 +93,8 @@
             setImageLevel(icon.iconLevel);
         }
         if (!numberEquals) {
-            if (icon.number > 0) {
+            if (icon.number > 0 && mContext.getResources().getBoolean(
+                        R.bool.config_statusBarShowNumber)) {
                 if (mNumberBackground == null) {
                     mNumberBackground = getContext().getResources().getDrawable(
                             R.drawable.ic_notification_overlay);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 4ff2429..18003dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -37,6 +37,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
+import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -57,8 +58,6 @@
     boolean mHspaDataDistinguishable;
     final TelephonyManager mPhone;
     boolean mDataConnected;
-    int mPhoneSignalIconId;
-    int mDataIconId;
     IccCard.State mSimState = IccCard.State.READY;
     int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
     int mDataState = TelephonyManager.DATA_DISCONNECTED;
@@ -66,6 +65,10 @@
     ServiceState mServiceState;
     SignalStrength mSignalStrength;
     int[] mDataIconList = TelephonyIcons.DATA_G[0];
+    int mPhoneSignalIconId;
+    int mDataDirectionIconId;
+    int mDataSignalIconId;
+    int mDataTypeIconId;
  
     // wifi
     final WifiManager mWifiManager;
@@ -81,11 +84,17 @@
 
     // our ui
     Context mContext;
-    ArrayList<ImageView> mPhoneIconViews = new ArrayList<ImageView>();
-    ArrayList<ImageView> mDataIconViews = new ArrayList<ImageView>();
+    ArrayList<ImageView> mPhoneSignalIconViews = new ArrayList<ImageView>();
+    ArrayList<ImageView> mDataDirectionIconViews = new ArrayList<ImageView>();
+    ArrayList<ImageView> mWifiIconViews = new ArrayList<ImageView>();
+    ArrayList<ImageView> mCombinedSignalIconViews = new ArrayList<ImageView>();
+    ArrayList<ImageView> mDataTypeIconViews = new ArrayList<ImageView>();
     ArrayList<TextView> mLabelViews = new ArrayList<TextView>();
     int mLastPhoneSignalIconId = -1;
-    int mLastCombinedDataIconId = -1;
+    int mLastDataDirectionIconId = -1;
+    int mLastWifiIconId = -1;
+    int mLastCombinedSignalIconId = -1;
+    int mLastDataTypeIconId = -1;
     String mLastLabel = "";
     
     // yuck -- stop doing this here and put it in the framework
@@ -116,7 +125,6 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         context.registerReceiver(this, filter);
@@ -125,12 +133,24 @@
         mBatteryStats = BatteryStatsService.getService();
     }
 
-    public void addPhoneIconView(ImageView v) {
-        mPhoneIconViews.add(v);
+    public void addPhoneSignalIconView(ImageView v) {
+        mPhoneSignalIconViews.add(v);
     }
 
-    public void addCombinedDataIconView(ImageView v) {
-        mDataIconViews.add(v);
+    public void addDataDirectionIconView(ImageView v) {
+        mDataDirectionIconViews.add(v);
+    }
+
+    public void addWifiIconView(ImageView v) {
+        mWifiIconViews.add(v);
+    }
+
+    public void addCombinedSignalIconView(ImageView v) {
+        mCombinedSignalIconViews.add(v);
+    }
+
+    public void addDataTypeIconView(ImageView v) {
+        mDataTypeIconViews.add(v);
     }
 
     public void addLabelView(TextView v) {
@@ -141,7 +161,6 @@
         final String action = intent.getAction();
         if (action.equals(WifiManager.RSSI_CHANGED_ACTION)
                 || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)
-                || action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)
                 || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
             updateWifiState(intent);
             refreshViews();
@@ -317,12 +336,15 @@
             if (Settings.System.getInt(mContext.getContentResolver(),
                     Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
                 mPhoneSignalIconId = R.drawable.stat_sys_signal_flightmode;
+                mDataSignalIconId = R.drawable.stat_sys_signal_flightmode;
             } else {
                 mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
+                mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null
             }
         } else {
             if (mSignalStrength == null) {
                 mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
+                mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null
             } else if (isCdma()) {
                 // If 3G(EV) and 1x network are available than 3G should be
                 // displayed, displayed RSSI should be from the EV side.
@@ -340,6 +362,7 @@
                     iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
                 }
                 mPhoneSignalIconId = iconList[iconLevel];
+                mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
             } else {
                 int asu = mSignalStrength.getGsmSignalStrength();
 
@@ -362,6 +385,7 @@
                     iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
                 }
                 mPhoneSignalIconId = iconList[iconLevel];
+                mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
             }
         }
     }
@@ -370,36 +394,47 @@
         switch (net) {
             case TelephonyManager.NETWORK_TYPE_EDGE:
                 mDataIconList = TelephonyIcons.DATA_E[mInetCondition];
+                mDataTypeIconId = R.drawable.stat_sys_signal_edge;
                 break;
             case TelephonyManager.NETWORK_TYPE_UMTS:
                 mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+                mDataTypeIconId = R.drawable.stat_sys_signal_3g;
                 break;
             case TelephonyManager.NETWORK_TYPE_HSDPA:
             case TelephonyManager.NETWORK_TYPE_HSUPA:
             case TelephonyManager.NETWORK_TYPE_HSPA:
                 if (mHspaDataDistinguishable) {
                     mDataIconList = TelephonyIcons.DATA_H[mInetCondition];
+                    mDataTypeIconId = R.drawable.stat_sys_signal_hsdpa;
                 } else {
                     mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+                    mDataTypeIconId = R.drawable.stat_sys_signal_3g;
                 }
                 break;
             case TelephonyManager.NETWORK_TYPE_CDMA:
                 // display 1xRTT for IS95A/B
                 mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
+                mDataTypeIconId = R.drawable.stat_sys_signal_1x;
                 break;
             case TelephonyManager.NETWORK_TYPE_1xRTT:
                 mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
+                mDataTypeIconId = R.drawable.stat_sys_signal_1x;
                 break;
             case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
             case TelephonyManager.NETWORK_TYPE_EVDO_A:
             case TelephonyManager.NETWORK_TYPE_EVDO_B:
                 mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+                mDataTypeIconId = R.drawable.stat_sys_signal_3g;
                 break;
             // TODO - add support for NETWORK_TYPE_LTE and NETWORK_TYPE_EHRPD
             default:
                 mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
+                mDataTypeIconId = R.drawable.stat_sys_signal_gprs;
             break;
         }
+        if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) {
+            mDataTypeIconId = R.drawable.stat_sys_signal_roam;
+        }
     }
 
     boolean isCdmaEri() {
@@ -436,7 +471,7 @@
                             iconId = mDataIconList[0];
                             break;
                     }
-                    mDataIconId = iconId;
+                    mDataDirectionIconId = iconId;
                 } else {
                     iconId = 0;
                     visible = false;
@@ -477,7 +512,7 @@
             Binder.restoreCallingIdentity(ident);
         }
 
-        mDataIconId = iconId;
+        mDataDirectionIconId = iconId;
         mDataConnected = visible;
     }
 
@@ -489,8 +524,7 @@
             mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                     WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
 
-        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)
-                || action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
+        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
             final NetworkInfo networkInfo = (NetworkInfo)
                     intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
             boolean wasConnected = mWifiConnected;
@@ -575,8 +609,10 @@
     void refreshViews() {
         Context context = mContext;
 
-        int combinedDataIconId;
+        int combinedSignalIconId;
+        int dataTypeIconId;
         String label;
+        int N;
 
         if (mWifiConnected) {
             if (mWifiSsid == null) {
@@ -585,35 +621,84 @@
                 label = context.getString(R.string.system_panel_signal_meter_wifi_ssid_format,
                                       mWifiSsid);
             }
-            combinedDataIconId = mWifiIconId;
-        } else if (mDataConnected) {
-            label = context.getString(R.string.system_panel_signal_meter_data_connected);
-            combinedDataIconId = mDataIconId;
+            combinedSignalIconId = mWifiIconId;
+            dataTypeIconId = 0;
         } else {
-            label = context.getString(R.string.system_panel_signal_meter_disconnected);
-            combinedDataIconId = 0;
+            if (mDataConnected) {
+                label = context.getString(R.string.system_panel_signal_meter_data_connected);
+            } else {
+                label = context.getString(R.string.system_panel_signal_meter_disconnected);
+            }
+            combinedSignalIconId = mDataSignalIconId;
+            dataTypeIconId = mDataTypeIconId;
         }
 
-        int N;
+        if (false) {
+            Slog.d(TAG, "refreshViews combinedSignalIconId=0x"
+                    + Integer.toHexString(mPhoneSignalIconId)
+                    + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId)
+                    + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId)
+                    + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId)
+                    + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId)
+                    + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId));
+        }
 
+        // the phone icon on phones
         if (mLastPhoneSignalIconId != mPhoneSignalIconId) {
             mLastPhoneSignalIconId = mPhoneSignalIconId;
-            N = mPhoneIconViews.size();
+            N = mPhoneSignalIconViews.size();
             for (int i=0; i<N; i++) {
-                ImageView v = mPhoneIconViews.get(i);
+                final ImageView v = mPhoneSignalIconViews.get(i);
                 v.setImageResource(mPhoneSignalIconId);
             }
         }
 
-        if (mLastCombinedDataIconId != combinedDataIconId) {
-            mLastCombinedDataIconId = combinedDataIconId;
-            N = mDataIconViews.size();
+        // the data icon on phones
+        if (mLastDataDirectionIconId != mDataDirectionIconId) {
+            mLastDataDirectionIconId = mDataDirectionIconId;
+            N = mDataDirectionIconViews.size();
             for (int i=0; i<N; i++) {
-                ImageView v = mDataIconViews.get(i);
-                v.setImageResource(combinedDataIconId);
+                final ImageView v = mDataDirectionIconViews.get(i);
+                v.setImageResource(mDataDirectionIconId);
             }
         }
 
+        // the wifi icon on phones
+        if (mLastWifiIconId != mWifiIconId) {
+            mLastWifiIconId = mWifiIconId;
+            N = mWifiIconViews.size();
+            for (int i=0; i<N; i++) {
+                final ImageView v = mWifiIconViews.get(i);
+                v.setImageResource(mWifiIconId);
+            }
+        }
+
+        // the combined data signal icon
+        if (mLastCombinedSignalIconId != combinedSignalIconId) {
+            mLastCombinedSignalIconId = combinedSignalIconId;
+            N = mCombinedSignalIconViews.size();
+            for (int i=0; i<N; i++) {
+                final ImageView v = mCombinedSignalIconViews.get(i);
+                v.setImageResource(combinedSignalIconId);
+            }
+        }
+
+        // the data network type overlay
+        if (mLastDataTypeIconId != dataTypeIconId) {
+            mLastDataTypeIconId = dataTypeIconId;
+            N = mDataTypeIconViews.size();
+            for (int i=0; i<N; i++) {
+                final ImageView v = mDataTypeIconViews.get(i);
+                if (dataTypeIconId == 0) {
+                    v.setVisibility(View.INVISIBLE);
+                } else {
+                    v.setVisibility(View.VISIBLE);
+                    v.setImageResource(dataTypeIconId);
+                }
+            }
+        }
+
+        // the label in the notification panel
         if (!mLastLabel.equals(label)) {
             mLastLabel = label;
             N = mLabelViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index 050a746..94c68ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -48,6 +48,8 @@
           R.drawable.stat_sys_r_signal_4_fully }
     };
 
+    static final int[][] DATA_SIGNAL_STRENGTH = TELEPHONY_SIGNAL_STRENGTH;
+
     //***** Data connection icons
 
     //GSM/UMTS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
index 80cb5b2..5f49d8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
@@ -19,16 +19,16 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.util.Slog;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 
-public class NotificationPanel extends RelativeLayout implements StatusBarPanel,
+public class NotificationPanel extends LinearLayout implements StatusBarPanel,
         View.OnClickListener {
     static final String TAG = "NotificationPanel";
 
@@ -71,6 +71,29 @@
         }
     }
 
+    /**
+     * We need to be aligned at the bottom.  LinearLayout can't do this, so instead,
+     * let LinearLayout do all the hard work, and then shift everything down to the bottom.
+     */
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        // We know that none of our children are GONE, so don't worry about skipping GONE views.
+        final int N = getChildCount();
+        if (N == 0) {
+            return;
+        }
+        final int allocatedBottom = getChildAt(N-1).getBottom();
+        final int shift = b - allocatedBottom - getPaddingBottom();
+        if (shift <= 0) {
+            return;
+        }
+        for (int i=0; i<N; i++) {
+            final View c = getChildAt(i);
+            c.layout(c.getLeft(), c.getTop() + shift, c.getRight(), c.getBottom() + shift);
+        }
+    }
+
     public void onClick(View v) {
         if (v == mSettingsButton) {
             switchToSettingsMode();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
index e0b05f9..0c31304 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -252,13 +252,13 @@
         ActivityDescription ad = (ActivityDescription)v.getTag();
         if (ad.id >= 0) {
             // This is an active task; it should just go to the foreground.
-            IActivityManager am = ActivityManagerNative.getDefault();
-            try {
-                am.moveTaskToFront(ad.id);
-            } catch (RemoteException e) {
-            }
+            final ActivityManager am = (ActivityManager)
+                    getContext().getSystemService(Context.ACTIVITY_SERVICE);
+            am.moveTaskToFront(ad.id, ActivityManager.MOVE_TASK_WITH_HOME);
         } else {
             Intent intent = ad.intent;
+            intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+                    | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
             if (DEBUG) Log.v(TAG, "Starting activity " + intent);
             getContext().startActivity(intent);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 233ac45..a934cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -132,8 +132,6 @@
     NotificationIconArea.IconLayout mIconLayout;
 
     TabletTicker mTicker;
-    View mTickerView;
-    boolean mTicking;
 
     // for disabling the status bar
     int mDisabled = 0;
@@ -159,7 +157,7 @@
         mBatteryController.addIconView((ImageView)mNotificationPanel.findViewById(R.id.battery));
         mBatteryController.addLabelView(
                 (TextView)mNotificationPanel.findViewById(R.id.battery_text));
-        mNetworkController.addCombinedDataIconView(
+        mNetworkController.addCombinedSignalIconView(
                 (ImageView)mNotificationPanel.findViewById(R.id.network));
         mNetworkController.addLabelView(
                 (TextView)mNotificationPanel.findViewById(R.id.network_text));
@@ -167,7 +165,7 @@
         mStatusBarView.setIgnoreChildren(0, mNotificationTrigger, mNotificationPanel);
 
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
@@ -278,13 +276,13 @@
         mNotificationPeekTapDuration = vc.getTapTimeout();
         mNotificationFlingVelocity = 300; // px/s
 
-        mTicker = new TabletTicker(context, (FrameLayout)sb.findViewById(R.id.ticker));
+        mTicker = new TabletTicker(context);
 
         // The icons
         mBatteryController = new BatteryController(mContext);
         mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery));
         mNetworkController = new NetworkController(mContext);
-        mNetworkController.addCombinedDataIconView((ImageView)sb.findViewById(R.id.network));
+        mNetworkController.addCombinedSignalIconView((ImageView)sb.findViewById(R.id.network));
 
         // The navigation buttons
         mNavigationArea = sb.findViewById(R.id.navigationArea);
@@ -385,9 +383,7 @@
                     if (DEBUG) Slog.d(TAG, "opening notifications panel");
                     if (mNotificationPanel.getVisibility() == View.GONE) {
                         mNotificationPeekWindow.setVisibility(View.GONE);
-
                         mNotificationPanel.setVisibility(View.VISIBLE);
-
                         // synchronize with current shadow state
                         mShadowController.hideElement(mNotificationArea);
                     }
@@ -396,7 +392,6 @@
                     if (DEBUG) Slog.d(TAG, "closing notifications panel");
                     if (mNotificationPanel.getVisibility() == View.VISIBLE) {
                         mNotificationPanel.setVisibility(View.GONE);
-
                         // synchronize with current shadow state
                         mShadowController.showElement(mNotificationArea);
                     }
@@ -464,7 +459,7 @@
         boolean immersive = false;
         try {
             immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
-            Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
+            //Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
         } catch (RemoteException ex) {
         }
         if (false && immersive) {
@@ -603,9 +598,7 @@
     }
 
     private boolean hasTicker(Notification n) {
-        return !TextUtils.isEmpty(n.tickerText)
-                || !TextUtils.isEmpty(n.tickerTitle)
-                || !TextUtils.isEmpty(n.tickerSubtitle);
+        return n.tickerView != null || !TextUtils.isEmpty(n.tickerText);
     }
 
     private void tick(StatusBarNotification n) {
@@ -1005,13 +998,21 @@
                         } catch (RemoteException ex) {
                             // system process is dead if we're here.
                         }
-    //                    animateCollapse();
                     }
                 });
         } else {
             vetoButton.setVisibility(View.INVISIBLE);
         }
 
+        // the large icon
+        ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon);
+        if (sbn.notification.largeIcon != null) {
+            largeIcon.setImageBitmap(sbn.notification.largeIcon);
+        } else {
+            largeIcon.getLayoutParams().width = 0;
+            largeIcon.setVisibility(View.INVISIBLE);
+        }
+
         // bind the click event to the content area
         ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
         // XXX: update to allow controls within notification views
@@ -1034,7 +1035,7 @@
             exception = e;
         }
         if (expanded == null) {
-            String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
+            final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
             Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
             return false;
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
index 3c3139f..7f743b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
@@ -16,9 +16,14 @@
 
 package com.android.systemui.statusbar.tablet;
 
+import java.util.Arrays;
+
 import android.app.Notification;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Message;
 import android.util.Slog;
@@ -26,16 +31,18 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarNotification;
 
 import com.android.systemui.R;
-
-import java.util.Arrays;
+import com.android.systemui.statusbar.StatusBarIconView;
 
 public class TabletTicker extends Handler {
     private static final String TAG = "StatusBar.TabletTicker";
@@ -45,17 +52,16 @@
     private static final int ADVANCE_DELAY = 5000; // 5 seconds
 
     private Context mContext;
-    private FrameLayout mParent;
 
+    private ViewGroup mWindow;
     private StatusBarNotification mCurrentNotification;
     private View mCurrentView;
 
     private StatusBarNotification[] mQueue;
     private int mQueuePos;
 
-    public TabletTicker(Context context, FrameLayout parent) {
+    public TabletTicker(Context context) {
         mContext = context;
-        mParent = parent;
 
         // TODO: Make this a configuration value.
         // 3 is enough to let us see most cases, but not get so far behind that it's annoying.
@@ -102,18 +108,33 @@
     private void advance() {
         // Out with the old...
         if (mCurrentView != null) {
-            mParent.removeView(mCurrentView);
+            mWindow.removeView(mCurrentView);
             mCurrentView = null;
             mCurrentNotification = null;
         }
 
         // In with the new...
-        final StatusBarNotification next = dequeue();
-        if (next != null) {
+        StatusBarNotification next = dequeue();
+        while (next != null) {
             mCurrentNotification = next;
             mCurrentView = makeTickerView(next);
-            mParent.addView(mCurrentView);
-            sendEmptyMessageDelayed(MSG_ADVANCE, ADVANCE_DELAY);
+            if (mCurrentView != null) {
+                if (mWindow == null) {
+                    mWindow = makeWindow();
+                    WindowManagerImpl.getDefault().addView(mWindow, mWindow.getLayoutParams());
+                }
+                mWindow.addView(mCurrentView);
+                sendEmptyMessageDelayed(MSG_ADVANCE, ADVANCE_DELAY);
+                break;
+            }
+            next = dequeue();
+        }
+
+        // if there's nothing left, close the window
+        // TODO: Do this when the animation is done instead
+        if (mCurrentView == null) {
+            WindowManagerImpl.getDefault().removeView(mWindow);
+            mWindow = null;
         }
     }
 
@@ -133,46 +154,76 @@
         return notification;
     }
 
+    private ViewGroup makeWindow() {
+        final Resources res = mContext.getResources();
+        final FrameLayout view = new FrameLayout(mContext);
+        final int width = res.getDimensionPixelSize(R.dimen.notification_ticker_width);
+        final int height = res.getDimensionPixelSize(R.dimen.notification_large_icon_height);
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+                PixelFormat.TRANSLUCENT);
+        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+        lp.setTitle("NotificationTicker");
+        view.setLayoutParams(lp);
+        return view;
+    }
+
     private View makeTickerView(StatusBarNotification notification) {
         final Notification n = notification.notification;
 
         LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
 
-        int layoutId;
         ViewGroup group;
-        if (n.tickerTitle != null || n.tickerSubtitle != null) {
-            group = (ViewGroup)inflater.inflate(R.layout.ticker, mParent, false);
-            if (n.tickerTitle != null) {
-                final TextView title = (TextView)group.findViewById(R.id.title);
-                title.setText(n.tickerTitle);
-            }
-            if (n.tickerSubtitle != null) {
-                final TextView subtitle = (TextView)group.findViewById(R.id.subtitle);
-                subtitle.setText(n.tickerSubtitle);
-            }
+        int layoutId;
+        int iconId;
+        if (n.largeIcon != null) {
+            iconId = R.id.right_icon;
         } else {
-            group = (ViewGroup)inflater.inflate(R.layout.ticker_compat, mParent, false);
+            iconId = R.id.left_icon;
+        }
+        if (n.tickerView != null) {
+            group = (ViewGroup)inflater.inflate(R.layout.ticker, null, false);
+            View expanded = null;
+            Exception exception = null;
+            try {
+                expanded = n.tickerView.apply(mContext, group);
+            }
+            catch (RuntimeException e) {
+                exception = e;
+            }
+            if (expanded == null) {
+                final String ident = notification.pkg
+                        + "/0x" + Integer.toHexString(notification.id);
+                Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+                return null;
+            }
+            final int statusBarHeight = mContext.getResources().getDimensionPixelSize(
+                    com.android.internal.R.dimen.status_bar_height);
+            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT, statusBarHeight, 1.0f);
+            lp.gravity = Gravity.BOTTOM;
+            group.addView(expanded, lp);
+        } else if (n.tickerText != null) {
+            group = (ViewGroup)inflater.inflate(R.layout.ticker_compat, mWindow, false);
+            final Drawable icon = StatusBarIconView.getIcon(mContext,
+                    new StatusBarIcon(notification.pkg, n.icon, n.iconLevel, 0));
+            ImageView iv = (ImageView)group.findViewById(iconId);
+            iv.setImageDrawable(icon);
+            iv.setVisibility(View.VISIBLE);
             TextView tv = (TextView)group.findViewById(R.id.text);
             tv.setText(n.tickerText);
+        } else {
+            throw new RuntimeException("tickerView==null && tickerText==null");
         }
-
-        // No more than 2 icons.
-        if (n.tickerIcons != null) {
-            int N = n.tickerIcons.length;
-            if (N > 2) {
-                N = 2;
-            }
-            for (int i=N-1; i>= 0; i--) {
-                Bitmap b = n.tickerIcons[i];
-                if (b != null) {
-                    ImageView iv = (ImageView)inflater.inflate(R.layout.ticker_icon, group, false);
-                    iv.setImageBitmap(b);
-                    group.addView(iv, 0);
-                }
-            }
+        ImageView largeIcon = (ImageView)group.findViewById(R.id.large_icon);
+        if (n.largeIcon != null) {
+            largeIcon.setImageBitmap(n.largeIcon);
+            largeIcon.setVisibility(View.VISIBLE);
         }
-
         return group;
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 5287289..1c1a46e 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -139,6 +139,10 @@
     static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
     static final int LONG_PRESS_POWER_SHUT_OFF = 2;
     
+    static final int LONG_PRESS_HOME_NOTHING = 0;
+    static final int LONG_PRESS_HOME_RECENT_DIALOG = 1;
+    static final int LONG_PRESS_HOME_RECENT_ACTIVITY = 2;
+
     // wallpaper is at the bottom, though the window manager may move it.
     static final int WALLPAPER_LAYER = 2;
     static final int APPLICATION_LAYER = 2;
@@ -327,8 +331,8 @@
     // Nothing to see here, move along...
     int mFancyRotationAnimation;
     
-    // Enable 3D recents based on config settings.
-    private Boolean mUse3dRecents;
+    // What we do when the user long presses on home
+    private int mLongPressOnHomeBehavior = -1;
 
     ShortcutManager mShortcutManager;
     PowerManager.WakeLock mBroadcastWakeLock;
@@ -565,8 +569,6 @@
              * the user lets go of the home key
              */
             mHomePressed = false;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
-            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
             showRecentAppsDialog();
         }
     };
@@ -576,16 +578,32 @@
      */
     void showRecentAppsDialog() {
         // We can't initialize this in init() since the configuration hasn't been loaded yet.
-        if (mUse3dRecents == null) {
-            mUse3dRecents = mContext.getResources().getBoolean(R.bool.config_enableRecentApps3D);
+        if (mLongPressOnHomeBehavior < 0) {
+            mLongPressOnHomeBehavior
+                    = mContext.getResources().getInteger(R.integer.config_longPressOnPowerBehavior);
+            if (mLongPressOnHomeBehavior < LONG_PRESS_HOME_NOTHING ||
+                    mLongPressOnHomeBehavior > LONG_PRESS_HOME_RECENT_ACTIVITY) {
+                mLongPressOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
+            }
+        }
+
+        if (mLongPressOnHomeBehavior != LONG_PRESS_HOME_NOTHING) {
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
         }
         
         // Use 3d Recents dialog
-        if (mUse3dRecents) {
+        if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_DIALOG) {
+            // Fallback to dialog if we fail to launch the above.
+            if (mRecentAppsDialog == null) {
+                mRecentAppsDialog = new RecentApplicationsDialog(mContext);
+            }
+            mRecentAppsDialog.show();
+        } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_ACTIVITY) {
             try {
                 Intent intent = new Intent();
                 intent.setClassName("com.android.systemui", 
-                        "com.android.systemui.statusbar.RecentApplicationsActivity");
+                        "com.android.systemui.recent.RecentApplicationsActivity");
                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 
                         | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                 mContext.startActivity(intent);
@@ -594,12 +612,6 @@
                 Log.e(TAG, "Failed to launch RecentAppsIntent", e);
             }
         }
-
-        // Fallback to dialog if we fail to launch the above.
-        if (mRecentAppsDialog == null) {
-            mRecentAppsDialog = new RecentApplicationsDialog(mContext);
-        }
-        mRecentAppsDialog.show();
     }
     
     /** {@inheritDoc} */
diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
index d9e8c2b..f53092d 100644
--- a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
+++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
@@ -138,13 +138,12 @@
                 RecentTag tag = (RecentTag)b.getTag();
                 if (tag.info.id >= 0) {
                     // This is an active task; it should just go to the foreground.
-                    IActivityManager am = ActivityManagerNative.getDefault();
-                    try {
-                        am.moveTaskToFront(tag.info.id);
-                    } catch (RemoteException e) {
-                    }
+                    final ActivityManager am = (ActivityManager)
+                            getContext().getSystemService(Context.ACTIVITY_SERVICE);
+                    am.moveTaskToFront(tag.info.id, ActivityManager.MOVE_TASK_WITH_HOME);
                 } else if (tag.intent != null) {
-                    tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+                    tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+                            | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
                     try {
                         getContext().startActivity(tag.intent);
                     } catch (ActivityNotFoundException e) {
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 40883bd..47599c8 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -473,10 +473,15 @@
     private final int getIcon(int level) {
         if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
             return com.android.internal.R.drawable.stat_sys_battery_charge;
-        } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING ||
-                mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING ||
-                mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
+        } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
             return com.android.internal.R.drawable.stat_sys_battery;
+        } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
+                || mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
+            if (isPowered() && mBatteryLevel >= 100) {
+                return com.android.internal.R.drawable.stat_sys_battery_charge;
+            } else {
+                return com.android.internal.R.drawable.stat_sys_battery;
+            }
         } else {
             return com.android.internal.R.drawable.stat_sys_battery_unknown;
         }
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 0e45145..0a28da7 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -343,16 +343,17 @@
 
             if ((entry.flags & DropBoxManager.IS_TEXT) != 0 && (doPrint || !doFile)) {
                 DropBoxManager.Entry dbe = null;
+                InputStreamReader isr = null;
                 try {
                     dbe = new DropBoxManager.Entry(
                              entry.tag, entry.timestampMillis, entry.file, entry.flags);
 
                     if (doPrint) {
-                        InputStreamReader r = new InputStreamReader(dbe.getInputStream());
+                        isr = new InputStreamReader(dbe.getInputStream());
                         char[] buf = new char[4096];
                         boolean newline = false;
                         for (;;) {
-                            int n = r.read(buf);
+                            int n = isr.read(buf);
                             if (n <= 0) break;
                             out.append(buf, 0, n);
                             newline = (buf[n - 1] == '\n');
@@ -376,6 +377,12 @@
                     Slog.e(TAG, "Can't read: " + entry.file, e);
                 } finally {
                     if (dbe != null) dbe.close();
+                    if (isr != null) {
+                        try {
+                            isr.close();
+                        } catch (IOException unused) {
+                        }
+                    }
                 }
             }
 
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 84bc100..eca37b7 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -55,6 +55,7 @@
 import android.os.IInterface;
 import android.os.Message;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -120,6 +121,9 @@
     // If IME doesn't support the system locale, the default subtype will be the first defined one.
     private static final int DEFAULT_SUBTYPE_ID = 0;
 
+    private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
+    private static final String SUBTYPE_MODE_VOICE = "voice";
+
     final Context mContext;
     final Handler mHandler;
     final InputMethodSettings mSettings;
@@ -235,6 +239,10 @@
      */
     private InputMethodSubtype mCurrentSubtype;
 
+    // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
+    private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
+            mShortcutInputMethodsAndSubtypes =
+                new HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>();
 
     /**
      * Set to true if our ServiceConnection is currently actively bound to
@@ -983,6 +991,7 @@
                 mCurMethodId = null;
                 unbindCurrentMethodLocked(true);
             }
+            mShortcutInputMethodsAndSubtypes.clear();
         } else {
             // There is no longer an input method set, so stop any current one.
             mCurMethodId = null;
@@ -1910,25 +1919,31 @@
         return NOT_A_SUBTYPE_ID;
     }
 
-    // If there are no selected subtypes, tries finding the most applicable one according to the
-    // current system locale
-    private int findApplicableSubtype(String id) {
-        InputMethodInfo imi = mMethodMap.get(id);
-        if (imi == null) {
-            return NOT_A_SUBTYPE_ID;
-        }
-        ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+    /**
+     * If there are no selected subtypes, tries finding the most applicable one according to the
+     * given locale.
+     * @param subtypes this function will search the most applicable subtype in subtypes
+     * @param mode subtypes will be filtered by mode
+     * @param locale subtypes will be filtered by locale
+     * @param defaultSubtypeId if this function can't find the most applicable subtype, it will
+     * return defaultSubtypeId
+     * @return the most applicable subtypeId
+     */
+    private int findLastResortApplicableSubtypeLocked(
+            List<InputMethodSubtype> subtypes, String mode, String locale, int defaultSubtypeId) {
         if (subtypes == null || subtypes.size() == 0) {
             return NOT_A_SUBTYPE_ID;
         }
-        final String locale = mContext.getResources().getConfiguration().locale.toString();
+        if (TextUtils.isEmpty(locale)) {
+            locale = mContext.getResources().getConfiguration().locale.toString();
+        }
         final String language = locale.substring(0, 2);
         boolean partialMatchFound = false;
-        int applicableSubtypeId = DEFAULT_SUBTYPE_ID;
+        int applicableSubtypeId = defaultSubtypeId;
         for (int i = 0; i < subtypes.size(); ++i) {
             final String subtypeLocale = subtypes.get(i).getLocale();
-            // An applicable subtype should be a keyboard subtype
-            if (subtypes.get(i).getMode().equalsIgnoreCase("keyboard")) {
+            // An applicable subtype should match "mode".
+            if (subtypes.get(i).getMode().equalsIgnoreCase(mode)) {
                 if (locale.equals(subtypeLocale)) {
                     // Exact match (e.g. system locale is "en_US" and subtype locale is "en_US")
                     applicableSubtypeId = i;
@@ -1950,6 +1965,69 @@
         return applicableSubtypeId;
     }
 
+    // If there are no selected shortcuts, tries finding the most applicable ones.
+    private Pair<InputMethodInfo, InputMethodSubtype>
+            findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) {
+        List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
+        InputMethodInfo mostApplicableIMI = null;
+        int mostApplicableSubtypeId = NOT_A_SUBTYPE_ID;
+        boolean foundInSystemIME = false;
+
+        // Search applicable subtype for each InputMethodInfo
+        for (InputMethodInfo imi: imis) {
+            int subtypeId = NOT_A_SUBTYPE_ID;
+            if (mCurrentSubtype != null) {
+                // 1. Search with the current subtype's locale and the enabled subtypes
+                subtypeId = findLastResortApplicableSubtypeLocked(
+                        mSettings.getEnabledInputMethodSubtypeListLocked(
+                        imi), mode, mCurrentSubtype.getLocale(), NOT_A_SUBTYPE_ID);
+                if (subtypeId == NOT_A_SUBTYPE_ID) {
+                    // 2. Search with the current subtype's locale and all subtypes
+                    subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+                            mode, mCurrentSubtype.getLocale(), NOT_A_SUBTYPE_ID);
+                }
+            }
+            // 3. Search with the system locale and the enabled subtypes
+            if (subtypeId == NOT_A_SUBTYPE_ID) {
+                subtypeId = findLastResortApplicableSubtypeLocked(
+                        mSettings.getEnabledInputMethodSubtypeListLocked(
+                        imi), mode, null, NOT_A_SUBTYPE_ID);
+            }
+            if (subtypeId == NOT_A_SUBTYPE_ID) {
+                // 4. Search with the system locale and all subtypes
+                subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+                        mode, null, NOT_A_SUBTYPE_ID);
+            }
+            if (subtypeId != NOT_A_SUBTYPE_ID) {
+                if (imi.getId().equals(mCurMethodId)) {
+                    // The current input method is the most applicable IME.
+                    mostApplicableIMI = imi;
+                    mostApplicableSubtypeId = subtypeId;
+                    break;
+                } else if ((imi.getServiceInfo().applicationInfo.flags
+                        & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    // The system input method is 2nd applicable IME.
+                    mostApplicableIMI = imi;
+                    mostApplicableSubtypeId = subtypeId;
+                    foundInSystemIME = true;
+                } else if (!foundInSystemIME) {
+                    mostApplicableIMI = imi;
+                    mostApplicableSubtypeId = subtypeId;
+                }
+            }
+        }
+        if (DEBUG) {
+            Slog.w(TAG, "Most applicable shortcut input method subtype was:"
+                    + mostApplicableIMI.getId() + "," + mostApplicableSubtypeId);
+        }
+        if (mostApplicableIMI != null && mostApplicableSubtypeId != NOT_A_SUBTYPE_ID) {
+            return new Pair<InputMethodInfo, InputMethodSubtype> (mostApplicableIMI,
+                    mostApplicableIMI.getSubtypes().get(mostApplicableSubtypeId));
+        } else {
+            return null;
+        }
+    }
+
     /**
      * @return Return the current subtype of this input method.
      */
@@ -1960,18 +2038,65 @@
                     Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE) != NOT_A_SUBTYPE_ID;
         } catch (SettingNotFoundException e) {
         }
-        if (!subtypeIsSelected || mCurrentSubtype == null) {
-            String lastInputMethodId = Settings.Secure.getString(mContext
-                    .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
-            int subtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
-            if (subtypeId == NOT_A_SUBTYPE_ID) {
-                subtypeId = findApplicableSubtype(lastInputMethodId);
+        synchronized (mMethodMap) {
+            if (!subtypeIsSelected || mCurrentSubtype == null) {
+                String lastInputMethodId = Settings.Secure.getString(
+                        mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+                int subtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
+                if (subtypeId == NOT_A_SUBTYPE_ID) {
+                    InputMethodInfo imi = mMethodMap.get(lastInputMethodId);
+                    if (imi != null) {
+                        // If there are no selected subtypes, the framework will try to find
+                        // the most applicable subtype from all subtypes whose mode is
+                        // SUBTYPE_MODE_KEYBOARD. This is an exceptional case, so we will hardcode
+                        // the mode.
+                        subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+                                SUBTYPE_MODE_KEYBOARD, null, DEFAULT_SUBTYPE_ID);
+                    }
+                }
+                if (subtypeId != NOT_A_SUBTYPE_ID) {
+                    mCurrentSubtype =
+                            mMethodMap.get(lastInputMethodId).getSubtypes().get(subtypeId);
+                } else {
+                    mCurrentSubtype = null;
+                }
             }
-            if (subtypeId != NOT_A_SUBTYPE_ID) {
-                mCurrentSubtype = mMethodMap.get(lastInputMethodId).getSubtypes().get(subtypeId);
-            }
+            return mCurrentSubtype;
         }
-        return mCurrentSubtype;
+    }
+
+    private void addShortcutInputMethodAndSubtypes(InputMethodInfo imi,
+            InputMethodSubtype subtype) {
+        if (mShortcutInputMethodsAndSubtypes.containsKey(imi)) {
+            mShortcutInputMethodsAndSubtypes.get(imi).add(subtype);
+        } else {
+            ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(subtype);
+            mShortcutInputMethodsAndSubtypes.put(imi, subtypes);
+        }
+    }
+
+    // TODO: We should change the return type from List to List<Parcelable>
+    public List getShortcutInputMethodsAndSubtypes() {
+        synchronized (mMethodMap) {
+            if (mShortcutInputMethodsAndSubtypes.size() == 0) {
+                // If there are no selected shortcut subtypes, the framework will try to find
+                // the most applicable subtype from all subtypes whose mode is
+                // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode.
+                Pair<InputMethodInfo, InputMethodSubtype> info =
+                    findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
+                            SUBTYPE_MODE_VOICE);
+                addShortcutInputMethodAndSubtypes(info.first, info.second);
+            }
+            ArrayList ret = new ArrayList<Object>();
+            for (InputMethodInfo imi: mShortcutInputMethodsAndSubtypes.keySet()) {
+                ret.add(imi);
+                for (InputMethodSubtype subtype: mShortcutInputMethodsAndSubtypes.get(imi)) {
+                    ret.add(subtype);
+                }
+            }
+            return ret;
+        }
     }
 
     public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 1081a20..6de7e6a 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -272,11 +272,13 @@
 
         public void onNotificationClick(String pkg, String tag, int id) {
             cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
-                    Notification.FLAG_FOREGROUND_SERVICE);
+                    Notification.FLAG_FOREGROUND_SERVICE, true);
         }
 
         public void onNotificationClear(String pkg, String tag, int id) {
-            cancelNotification(pkg, tag, id, 0, 0); // maybe add some flags?
+            cancelNotification(pkg, tag, id, 0,
+                Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+                true);
         }
 
         public void onPanelRevealed() {
@@ -312,7 +314,7 @@
                 int uid, int initialPid, String message) {
             Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
                     + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
-            cancelNotification(pkg, tag, id, 0, 0);
+            cancelNotification(pkg, tag, id, 0, 0, false);
             long ident = Binder.clearCallingIdentity();
             try {
                 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
@@ -855,7 +857,20 @@
         manager.sendAccessibilityEvent(event);
     }
 
-    private void cancelNotificationLocked(NotificationRecord r) {
+    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
+        // tell the app
+        if (sendDelete) {
+            if (r.notification.deleteIntent != null) {
+                try {
+                    r.notification.deleteIntent.send();
+                } catch (PendingIntent.CanceledException ex) {
+                    // do nothing - there's no relevant way to recover, and
+                    //     no reason to let this propagate
+                    Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
+                }
+            }
+        }
+
         // status bar
         if (r.notification.icon != 0) {
             long identity = Binder.clearCallingIdentity();
@@ -904,7 +919,7 @@
      * and none of the {@code mustNotHaveFlags}.
      */
     private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
-            int mustNotHaveFlags) {
+            int mustNotHaveFlags, boolean sendDelete) {
         EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags);
 
         synchronized (mNotificationList) {
@@ -921,7 +936,7 @@
 
                 mNotificationList.remove(index);
 
-                cancelNotificationLocked(r);
+                cancelNotificationLocked(r, sendDelete);
                 updateLightsLocked();
             }
         }
@@ -954,7 +969,7 @@
                     return true;
                 }
                 mNotificationList.remove(i);
-                cancelNotificationLocked(r);
+                cancelNotificationLocked(r, false);
             }
             if (canceledSomething) {
                 updateLightsLocked();
@@ -973,7 +988,7 @@
         // Don't allow client applications to cancel foreground service notis.
         cancelNotification(pkg, tag, id, 0,
                 Binder.getCallingUid() == Process.SYSTEM_UID
-                ? 0 : Notification.FLAG_FOREGROUND_SERVICE);
+                ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false);
     }
 
     public void cancelAllNotifications(String pkg) {
@@ -1009,17 +1024,8 @@
 
                 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
                                 | Notification.FLAG_NO_CLEAR)) == 0) {
-                    if (r.notification.deleteIntent != null) {
-                        try {
-                            r.notification.deleteIntent.send();
-                        } catch (PendingIntent.CanceledException ex) {
-                            // do nothing - there's no relevant way to recover, and
-                            //     no reason to let this propagate
-                            Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
-                        }
-                    }
                     mNotificationList.remove(i);
-                    cancelNotificationLocked(r);
+                    cancelNotificationLocked(r, true);
                 }
             }
 
diff --git a/services/java/com/android/server/StrictModeFlash.java b/services/java/com/android/server/StrictModeFlash.java
new file mode 100644
index 0000000..0a6c625
--- /dev/null
+++ b/services/java/com/android/server/StrictModeFlash.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;  // TODO: use com.android.server.wm, once things move there
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceSession;
+
+class StrictModeFlash {
+    private static final String TAG = "StrictModeFlash";
+
+    Surface mSurface;
+    int mLastDW;
+    int mLastDH;
+    boolean mDrawNeeded;
+    final int mThickness = 20;
+
+    public StrictModeFlash(Display display, SurfaceSession session) {
+        final DisplayMetrics dm = new DisplayMetrics();
+        display.getMetrics(dm);
+
+        try {
+            mSurface = new Surface(session, 0, "StrictModeFlash", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
+        } catch (Surface.OutOfResourcesException e) {
+            return;
+        }
+
+        mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);  // one more than Watermark? arbitrary.
+        mSurface.setPosition(0, 0);
+        mDrawNeeded = true;
+    }
+
+    private void drawIfNeeded() {
+        if (!mDrawNeeded) {
+            return;
+        }
+        mDrawNeeded = false;
+        final int dw = mLastDW;
+        final int dh = mLastDH;
+
+        Rect dirty = new Rect(0, 0, dw, dh);
+        Canvas c = null;
+        try {
+            c = mSurface.lockCanvas(dirty);
+        } catch (IllegalArgumentException e) {
+        } catch (Surface.OutOfResourcesException e) {
+        }
+        if (c == null) {
+            return;
+        }
+
+        // Top
+        c.clipRect(new Rect(0, 0, dw, mThickness), Region.Op.REPLACE);
+        c.drawColor(Color.RED);
+        // Left
+        c.clipRect(new Rect(0, 0, mThickness, dh), Region.Op.REPLACE);
+        c.drawColor(Color.RED);
+        // Right
+        c.clipRect(new Rect(dw - mThickness, 0, dw, dh), Region.Op.REPLACE);
+        c.drawColor(Color.RED);
+        // Bottom
+        c.clipRect(new Rect(0, dh - mThickness, dw, dh), Region.Op.REPLACE);
+        c.drawColor(Color.RED);
+
+        mSurface.unlockCanvasAndPost(c);
+    }
+
+    // Note: caller responsible for being inside
+    // Surface.openTransaction() / closeTransaction()
+    public void setVisibility(boolean on) {
+        if (mSurface == null) {
+            return;
+        }
+        drawIfNeeded();
+        if (on) {
+            mSurface.show();
+        } else {
+            mSurface.hide();
+        }
+    }
+
+    void positionSurface(int dw, int dh) {
+        if (mLastDW == dw && mLastDH == dh) {
+            return;
+        }
+        mLastDW = dw;
+        mLastDH = dh;
+        mSurface.setSize(dw, dh);
+        mDrawNeeded = true;
+    }
+
+}
diff --git a/services/java/com/android/server/UsbObserver.java b/services/java/com/android/server/UsbObserver.java
index cfa83be..4a7df8f 100644
--- a/services/java/com/android/server/UsbObserver.java
+++ b/services/java/com/android/server/UsbObserver.java
@@ -24,7 +24,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.UEventObserver;
-import android.provider.Mtp;
+import android.provider.Ptp;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
@@ -155,7 +155,7 @@
     // called from JNI in monitorUsbHostBus()
     private void usbCameraAdded(int deviceID) {
         Intent intent = new Intent(Usb.ACTION_USB_CAMERA_ATTACHED,
-                                Mtp.Device.getContentUri(deviceID));
+                                Ptp.Device.getContentUri(deviceID));
         Log.d(TAG, "usbCameraAdded, sending " + intent);
         mContext.sendBroadcast(intent);
     }
@@ -163,7 +163,7 @@
     // called from JNI in monitorUsbHostBus()
     private void usbCameraRemoved(int deviceID) {
         Intent intent = new Intent(Usb.ACTION_USB_CAMERA_DETACHED,
-                                Mtp.Device.getContentUri(deviceID));
+                                Ptp.Device.getContentUri(deviceID));
         Log.d(TAG, "usbCameraRemoved, sending " + intent);
         mContext.sendBroadcast(intent);
     }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index ec05437..b3ea836 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -59,6 +59,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -200,10 +201,6 @@
      */
     static final int DEFAULT_FADE_IN_OUT_DURATION = 400;
 
-    /** Adjustment to time to perform a dim, to make it more dramatic.
-     */
-    static final int DIM_DURATION_MULTIPLIER = 6;
-    
     // Maximum number of milliseconds to wait for input event injection.
     // FIXME is this value reasonable?
     private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
@@ -376,6 +373,7 @@
     Surface mBlurSurface;
     boolean mBlurShown;
     Watermark mWatermark;
+    StrictModeFlash mStrictModeFlash;
 
     int mTransactionSequence = 0;
 
@@ -4883,6 +4881,36 @@
         }
     }
 
+    // TODO: more accounting of which pid(s) turned it on, keep count,
+    // only allow disables from pids which have count on, etc.
+    public void showStrictModeViolation(boolean on) {
+        int pid = Binder.getCallingPid();
+        synchronized(mWindowMap) {
+            // Ignoring requests to enable the red border from clients
+            // which aren't on screen.  (e.g. Broadcast Receivers in
+            // the background..)
+            if (on) {
+                boolean isVisible = false;
+                for (WindowState ws : mWindows) {
+                    if (ws.mSession.mPid == pid && ws.isVisibleLw()) {
+                        isVisible = true;
+                        break;
+                    }
+                }
+                if (!isVisible) {
+                    return;
+                }
+            }
+
+            Surface.openTransaction();
+            if (mStrictModeFlash == null) {
+                mStrictModeFlash = new StrictModeFlash(mDisplay, mFxSession);
+            }
+            mStrictModeFlash.setVisibility(on);
+            Surface.closeTransaction();
+        }
+    }
+
     public void freezeRotation() {
         if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                 "setRotation()")) {
@@ -6596,6 +6624,7 @@
         final Rect mContainingFrame = new Rect();
         final Rect mDisplayFrame = new Rect();
         final Rect mContentFrame = new Rect();
+        final Rect mParentFrame = new Rect();
         final Rect mVisibleFrame = new Rect();
 
         boolean mContentChanged;
@@ -6801,8 +6830,12 @@
                 h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight;
             }
 
+            if (!mParentFrame.equals(pf)) {
+                mParentFrame.set(pf);
+                mContentChanged = true;
+            }
+
             final Rect content = mContentFrame;
-            mContentChanged |= !content.equals(cf);
             content.set(cf);
 
             final Rect visible = mVisibleFrame;
@@ -7668,6 +7701,21 @@
                     && !mDrawPending && !mCommitDrawPending;
         }
 
+        /**
+         * Return whether this window is wanting to have a translation
+         * animation applied to it for an in-progress move.  (Only makes
+         * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
+         */
+        boolean shouldAnimateMove() {
+            return mContentChanged && !mAnimating && !mLastHidden && !mDisplayFrozen
+                    && (mFrame.top != mLastFrame.top
+                            || mFrame.left != mLastFrame.left)
+                    && (mAttachedWindow == null
+                            || (mAttachedWindow.mAnimation == null
+                                    && !mAttachedWindow.shouldAnimateMove()))
+                    && mPolicy.isScreenOn();
+        }
+
         boolean needsBackgroundFiller(int screenWidth, int screenHeight) {
             return
                  // only if the application is requesting compatible window
@@ -7894,6 +7942,8 @@
                     pw.println();
             pw.print(prefix); pw.print("mContainingFrame=");
                     mContainingFrame.printShortString(pw);
+                    pw.print(" mParentFrame=");
+                    mParentFrame.printShortString(pw);
                     pw.print(" mDisplayFrame=");
                     mDisplayFrame.printShortString(pw);
                     pw.println();
@@ -9126,11 +9176,11 @@
                     || win.mAttachedHidden
                     || win.mExiting || win.mDestroying;
 
-            if (!win.mLayoutAttached) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "First pass " + win
+            if (DEBUG_LAYOUT && !win.mLayoutAttached) {
+                Slog.v(TAG, "First pass " + win
                         + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
                         + " mLayoutAttached=" + win.mLayoutAttached);
-                if (DEBUG_LAYOUT && gone) Slog.v(TAG, "  (mViewVisibility="
+                if (gone) Slog.v(TAG, "  (mViewVisibility="
                         + win.mViewVisibility + " mRelayoutCalled="
                         + win.mRelayoutCalled + " hidden="
                         + win.mRootToken.hidden + " hiddenRequested="
@@ -9167,16 +9217,16 @@
         for (i = topAttached; i >= 0; i--) {
             WindowState win = mWindows.get(i);
 
-            // If this view is GONE, then skip it -- keep the current
-            // frame, and let the caller know so they can ignore it
-            // if they want.  (We do the normal layout for INVISIBLE
-            // windows, since that means "perform layout as normal,
-            // just don't display").
             if (win.mLayoutAttached) {
                 if (DEBUG_LAYOUT) Slog.v(TAG, "Second pass " + win
                         + " mHaveFrame=" + win.mHaveFrame
                         + " mViewVisibility=" + win.mViewVisibility
                         + " mRelayoutCalled=" + win.mRelayoutCalled);
+                // If this view is GONE, then skip it -- keep the current
+                // frame, and let the caller know so they can ignore it
+                // if they want.  (We do the normal layout for INVISIBLE
+                // windows, since that means "perform layout as normal,
+                // just don't display").
                 if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
                         || !win.mHaveFrame) {
                     if (initial) {
@@ -9198,6 +9248,7 @@
         return mPolicy.finishLayoutLw();
     }
 
+    // "Something has changed!  Let's make it correct now."
     private final void performLayoutAndPlaceSurfacesLockedInner(
             boolean recoveringMemory) {
         if (mDisplay == null) {
@@ -9249,6 +9300,9 @@
         if (mWatermark != null) {
             mWatermark.positionSurface(dw, dh);
         }
+        if (mStrictModeFlash != null) {
+            mStrictModeFlash.positionSurface(dw, dh);
+        }
 
         try {
             boolean wallpaperForceHidingChanged = false;
@@ -9354,11 +9408,8 @@
                         // If the window has moved due to its containing
                         // content frame changing, then we'd like to animate
                         // it.  The checks here are ordered by what is least
-                        //Êlikely to be true first.
-                        if (w.mContentChanged && !wasAnimating && !w.mLastHidden && !mDisplayFrozen
-                                && (w.mFrame.top != w.mLastFrame.top
-                                        || w.mFrame.left != w.mLastFrame.left)
-                                && mPolicy.isScreenOn()) {
+                        // likely to be true first.
+                        if (w.shouldAnimateMove()) {
                             // Frame has moved, containing content frame
                             // has also moved, and we're not currently animating...
                             // let's do something.
@@ -9367,6 +9418,7 @@
                             w.setAnimation(a);
                             animDw = w.mLastFrame.left - w.mFrame.left;
                             animDh = w.mLastFrame.top - w.mFrame.top;
+                            w.mContentChanged = false;
                         }
 
                         // Execute animation.
@@ -10230,7 +10282,8 @@
                                     mDimAnimator = new DimAnimator(mFxSession);
                                 }
                                 mDimAnimator.show(dw, dh);
-                                mDimAnimator.updateParameters(w, currentTime);
+                                mDimAnimator.updateParameters(mContext.getResources(),
+                                        w, currentTime);
                             }
                         }
                         if ((attrFlags&FLAG_BLUR_BEHIND) != 0) {
@@ -11292,7 +11345,7 @@
          * Set's the dim surface's layer and update dim parameters that will be used in
          * {@link updateSurface} after all windows are examined.
          */
-        void updateParameters(WindowState w, long currentTime) {
+        void updateParameters(Resources res, WindowState w, long currentTime) {
             mDimSurface.setLayer(w.mAnimLayer-1);
 
             final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
@@ -11306,11 +11359,15 @@
                         ? w.mAnimation.computeDurationHint()
                         : DEFAULT_DIM_DURATION;
                 if (target > mDimTargetAlpha) {
-                    // This is happening behind the activity UI,
-                    // so we can make it run a little longer to
-                    // give a stronger impression without disrupting
-                    // the user.
-                    duration *= DIM_DURATION_MULTIPLIER;
+                    TypedValue tv = new TypedValue();
+                    res.getValue(com.android.internal.R.fraction.config_dimBehindFadeDuration,
+                            tv, true);
+                    if (tv.type == TypedValue.TYPE_FRACTION) {
+                        duration = (long)tv.getFraction((float)duration, (float)duration);
+                    } else if (tv.type >= TypedValue.TYPE_FIRST_INT
+                            && tv.type <= TypedValue.TYPE_LAST_INT) {
+                        duration = tv.data;
+                    }
                 }
                 if (duration < 1) {
                     // Don't divide by zero
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e815524..1a10cff 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -557,7 +557,7 @@
             = new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>();
 
     /**
-     * Fingerprints (String.hashCode()) of stack traces that we've
+     * Fingerprints (hashCode()) of stack traces that we've
      * already logged DropBox entries for.  Guarded by itself.  If
      * something (rogue user app) forces this over
      * MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared.
@@ -2026,7 +2026,7 @@
             if (app == null || app.instrumentationClass == null) {
                 intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                 mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
-                        null, null, 0, 0, 0, false, false);
+                        null, null, 0, 0, 0, false, false, null);
             }
         }
         
@@ -2082,7 +2082,7 @@
                     intent.setComponent(new ComponentName(
                             ri.activityInfo.packageName, ri.activityInfo.name));
                     mMainStack.startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
-                            null, null, 0, 0, 0, false, false);
+                            null, null, 0, 0, 0, false, false, null);
                 }
             }
         }
@@ -2121,13 +2121,13 @@
         }
         mPendingActivityLaunches.clear();
     }
-    
+
     public final int startActivity(IApplicationThread caller,
             Intent intent, String resolvedType, Uri[] grantedUriPermissions,
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug) {
-        return mMainStack.startActivityMayWait(caller, intent, resolvedType,
+        return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
                 requestCode, onlyIfNeeded, debug, null, null);
     }
@@ -2138,7 +2138,7 @@
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug) {
         WaitResult res = new WaitResult();
-        mMainStack.startActivityMayWait(caller, intent, resolvedType,
+        mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
                 requestCode, onlyIfNeeded, debug, res, null);
         return res;
@@ -2149,12 +2149,12 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug, Configuration config) {
-        return mMainStack.startActivityMayWait(caller, intent, resolvedType,
+        return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
                 requestCode, onlyIfNeeded, debug, null, config);
     }
 
-     public int startActivityIntentSender(IApplicationThread caller,
+    public int startActivityIntentSender(IApplicationThread caller,
             IntentSender intent, Intent fillInIntent, String resolvedType,
             IBinder resultTo, String resultWho, int requestCode,
             int flagsMask, int flagsValues) {
@@ -2267,7 +2267,7 @@
             // those are not yet exposed to user code, so there is no need.
             int res = mMainStack.startActivityLocked(r.app.thread, intent,
                     r.resolvedType, null, 0, aInfo, resultTo, resultWho,
-                    requestCode, -1, r.launchedFromUid, false, false);
+                    requestCode, -1, r.launchedFromUid, false, false, null);
             Binder.restoreCallingIdentity(origId);
 
             r.finishing = wasFinishing;
@@ -2289,38 +2289,28 @@
             throw new SecurityException(
                     "startActivityInPackage only available to the system");
         }
-        
-        final boolean componentSpecified = intent.getComponent() != null;
-        
-        // Don't modify the client's object!
-        intent = new Intent(intent);
 
-        // Collect information about the target of the Intent.
-        ActivityInfo aInfo;
-        try {
-            ResolveInfo rInfo =
-                AppGlobals.getPackageManager().resolveIntent(
-                        intent, resolvedType,
-                        PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
-            aInfo = rInfo != null ? rInfo.activityInfo : null;
-        } catch (RemoteException e) {
-            aInfo = null;
+        return mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
+                null, 0, resultTo, resultWho, requestCode, onlyIfNeeded, false, null, null);
+    }
+
+    public final int startActivities(IApplicationThread caller,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+        return mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo);
+    }
+
+    public final int startActivitiesInPackage(int uid,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+
+        // This is so super not safe, that only the system (or okay root)
+        // can do it.
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != 0 && callingUid != Process.myUid()) {
+            throw new SecurityException(
+                    "startActivityInPackage only available to the system");
         }
 
-        if (aInfo != null) {
-            // Store the found target back into the intent, because now that
-            // we have it we never want to do this again.  For example, if the
-            // user navigates back to this point in the history, we should
-            // always restart the exact same activity.
-            intent.setComponent(new ComponentName(
-                    aInfo.applicationInfo.packageName, aInfo.name));
-        }
-
-        synchronized(this) {
-            return mMainStack.startActivityLocked(null, intent, resolvedType,
-                    null, 0, aInfo, resultTo, resultWho, requestCode, -1, uid,
-                    onlyIfNeeded, componentSpecified);
-        }
+        return mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo);
     }
 
     final void addRecentTaskLocked(TaskRecord task) {
@@ -3890,16 +3880,30 @@
 
     public IIntentSender getIntentSender(int type,
             String packageName, IBinder token, String resultWho,
-            int requestCode, Intent intent, String resolvedType, int flags) {
+            int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
         // Refuse possible leaked file descriptors
-        if (intent != null && intent.hasFileDescriptors() == true) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-
-        if (type == INTENT_SENDER_BROADCAST) {
-            if ((intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+        if (intents != null) {
+            if (intents.length < 1) {
+                throw new IllegalArgumentException("Intents array length must be >= 1");
+            }
+            for (int i=0; i<intents.length; i++) {
+                Intent intent = intents[i];
+                if (intent == null) {
+                    throw new IllegalArgumentException("Null intent at index " + i);
+                }
+                if (intent.hasFileDescriptors()) {
+                    throw new IllegalArgumentException("File descriptors passed in Intent");
+                }
+                if (type == INTENT_SENDER_BROADCAST &&
+                        (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+                    throw new IllegalArgumentException(
+                            "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+                }
+                intents[i] = new Intent(intent);
+            }
+            if (resolvedTypes != null && resolvedTypes.length != intents.length) {
                 throw new IllegalArgumentException(
-                        "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+                        "Intent array length does not match resolvedTypes length");
             }
         }
         
@@ -3922,7 +3926,7 @@
                 }
                 
                 return getIntentSenderLocked(type, packageName, callingUid,
-                        token, resultWho, requestCode, intent, resolvedType, flags);
+                        token, resultWho, requestCode, intents, resolvedTypes, flags);
                 
             } catch (RemoteException e) {
                 throw new SecurityException(e);
@@ -3932,7 +3936,7 @@
     
     IIntentSender getIntentSenderLocked(int type,
             String packageName, int callingUid, IBinder token, String resultWho,
-            int requestCode, Intent intent, String resolvedType, int flags) {
+            int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
         ActivityRecord activity = null;
         if (type == INTENT_SENDER_ACTIVITY_RESULT) {
             int index = mMainStack.indexOfTokenLocked(token);
@@ -3953,14 +3957,24 @@
 
         PendingIntentRecord.Key key = new PendingIntentRecord.Key(
                 type, packageName, activity, resultWho,
-                requestCode, intent, resolvedType, flags);
+                requestCode, intents, resolvedTypes, flags);
         WeakReference<PendingIntentRecord> ref;
         ref = mIntentSenderRecords.get(key);
         PendingIntentRecord rec = ref != null ? ref.get() : null;
         if (rec != null) {
             if (!cancelCurrent) {
                 if (updateCurrent) {
-                    rec.key.requestIntent.replaceExtras(intent);
+                    if (rec.key.requestIntent != null) {
+                        rec.key.requestIntent.replaceExtras(intents != null ? intents[0] : null);
+                    }
+                    if (intents != null) {
+                        intents[intents.length-1] = rec.key.requestIntent;
+                        rec.key.allIntents = intents;
+                        rec.key.allResolvedTypes = resolvedTypes;
+                    } else {
+                        rec.key.allIntents = null;
+                        rec.key.allResolvedTypes = null;
+                    }
                 }
                 return rec;
             }
@@ -5006,7 +5020,7 @@
     /**
      * TODO: Add mController hook
      */
-    public void moveTaskToFront(int task) {
+    public void moveTaskToFront(int task, int flags) {
         enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
                 "moveTaskToFront()");
 
@@ -5021,6 +5035,11 @@
                 for (int i=0; i<N; i++) {
                     TaskRecord tr = mRecentTasks.get(i);
                     if (tr.taskId == task) {
+                        if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+                            // Caller wants the home activity moved with it.  To accomplish this,
+                            // we'll just move the home task to the top first.
+                            mMainStack.moveHomeToFrontLocked();
+                        }
                         mMainStack.moveTaskToFrontLocked(tr, null);
                         return;
                     }
@@ -5028,6 +5047,11 @@
                 for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
                     ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
                     if (hr.task.taskId == task) {
+                        if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+                            // Caller wants the home activity moved with it.  To accomplish this,
+                            // we'll just move the home task to the top first.
+                            mMainStack.moveHomeToFrontLocked();
+                        }
                         mMainStack.moveTaskToFrontLocked(hr.task, null);
                         return;
                     }
@@ -6647,7 +6671,7 @@
         ProcessRecord r = findAppProcess(app);
 
         if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
-            Integer stackFingerprint = info.crashInfo.stackTrace.hashCode();
+            Integer stackFingerprint = info.hashCode();
             boolean logIt = true;
             synchronized (mAlreadyLoggedViolatedStacks) {
                 if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 51dc84e..b4ea036 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -99,8 +99,8 @@
     static final int DESTROY_TIMEOUT = 10*1000;
     
     // How long until we reset a task when the user returns to it.  Currently
-    // 30 minutes.
-    static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
+    // disabled.
+    static final long ACTIVITY_INACTIVE_RESET_TIME = 0;
     
     // How long between activity launches that we consider safe to not warn
     // the user about an unexpected activity being launched on top.
@@ -1487,7 +1487,8 @@
             ActivityRecord newActivity) {
         boolean forceReset = (newActivity.info.flags
                 &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
-        if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
+        if (ACTIVITY_INACTIVE_RESET_TIME > 0
+                && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
             if ((newActivity.info.flags
                     &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
                 forceReset = true;
@@ -1573,8 +1574,7 @@
                             if (mService.mCurTask <= 0) {
                                 mService.mCurTask = 1;
                             }
-                            target.task = new TaskRecord(mService.mCurTask, target.info, null,
-                                    (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+                            target.task = new TaskRecord(mService.mCurTask, target.info, null);
                             target.task.affinityIntent = target.intent;
                             if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
                                     + " out to new task " + target.task);
@@ -1776,11 +1776,11 @@
      * activities on top of it and return the instance.
      *
      * @param newR Description of the new activity being started.
-     * @return Returns the old activity that should be continue to be used,
+     * @return Returns the old activity that should be continued to be used,
      * or null if none was found.
      */
     private final ActivityRecord performClearTaskLocked(int taskId,
-            ActivityRecord newR, int launchFlags, boolean doClear) {
+            ActivityRecord newR, int launchFlags) {
         int i = mHistory.size();
         
         // First find the requested task.
@@ -1806,17 +1806,18 @@
             if (r.realActivity.equals(newR.realActivity)) {
                 // Here it is!  Now finish everything in front...
                 ActivityRecord ret = r;
-                if (doClear) {
-                    while (i < (mHistory.size()-1)) {
-                        i++;
-                        r = (ActivityRecord)mHistory.get(i);
-                        if (r.finishing) {
-                            continue;
-                        }
-                        if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
-                                null, "clear")) {
-                            i--;
-                        }
+                while (i < (mHistory.size()-1)) {
+                    i++;
+                    r = (ActivityRecord)mHistory.get(i);
+                    if (r.task.taskId != taskId) {
+                        break;
+                    }
+                    if (r.finishing) {
+                        continue;
+                    }
+                    if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+                            null, "clear")) {
+                        i--;
                     }
                 }
                 
@@ -1843,6 +1844,51 @@
     }
 
     /**
+     * Completely remove all activities associated with an existing task.
+     */
+    private final void performClearTaskLocked(int taskId) {
+        int i = mHistory.size();
+
+        // First find the requested task.
+        while (i > 0) {
+            i--;
+            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            if (r.task.taskId == taskId) {
+                i++;
+                break;
+            }
+        }
+
+        // Now clear it.
+        while (i > 0) {
+            i--;
+            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            if (r.finishing) {
+                continue;
+            }
+            if (r.task.taskId != taskId) {
+                // We hit the bottom.  Now finish it all...
+                while (i < (mHistory.size()-1)) {
+                    i++;
+                    r = (ActivityRecord)mHistory.get(i);
+                    if (r.task.taskId != taskId) {
+                        // Whoops hit the end.
+                        return;
+                    }
+                    if (r.finishing) {
+                        continue;
+                    }
+                    if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+                            null, "clear")) {
+                        i--;
+                    }
+                }
+                return;
+            }
+        }
+    }
+
+    /**
      * Find the activity in the history stack within the given task.  Returns
      * the index within the history at which it's found, or < 0 if not found.
      */
@@ -1882,7 +1928,7 @@
             int grantedMode, ActivityInfo aInfo, IBinder resultTo,
             String resultWho, int requestCode,
             int callingPid, int callingUid, boolean onlyIfNeeded,
-            boolean componentSpecified) {
+            boolean componentSpecified, ActivityRecord[] outActivity) {
 
         int err = START_SUCCESS;
 
@@ -2004,6 +2050,9 @@
         ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
                 intent, resolvedType, aInfo, mService.mConfiguration,
                 resultRecord, resultWho, requestCode, componentSpecified);
+        if (outActivity != null) {
+            outActivity[0] = r;
+        }
 
         if (mMainStack) {
             if (mResumedActivity == null
@@ -2038,6 +2087,16 @@
                 grantedUriPermissions, grantedMode, onlyIfNeeded, true);
     }
   
+    final void moveHomeToFrontFromLaunchLocked(int launchFlags) {
+        if ((launchFlags &
+                (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
+                == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
+            // Caller wants to appear on home activity, so before starting
+            // their own activity we will bring home to the front.
+            moveHomeToFrontLocked();
+        }
+    }
+
     final int startActivityUncheckedLocked(ActivityRecord r,
             ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
             int grantedMode, boolean onlyIfNeeded, boolean doResume) {
@@ -2111,6 +2170,7 @@
         }
 
         boolean addingToTask = false;
+        TaskRecord reuseTask = null;
         if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
                 (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
                 || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
@@ -2148,6 +2208,7 @@
                         if (callerAtFront) {
                             // We really do want to push this one into the
                             // user's face, right now.
+                            moveHomeToFrontFromLaunchLocked(launchFlags);
                             moveTaskToFrontLocked(taskTop.task, r);
                         }
                     }
@@ -2166,7 +2227,16 @@
                         }
                         return START_RETURN_INTENT_TO_CALLER;
                     }
-                    if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
+                    if ((launchFlags &
+                            (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
+                            == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
+                        // The caller has requested to completely replace any
+                        // exising task with its new activity.  Well that should
+                        // not be too hard...
+                        reuseTask = taskTop.task;
+                        performClearTaskLocked(taskTop.task.taskId);
+                        reuseTask.setIntent(r.intent, r.info);
+                    } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
                             || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
                             || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                         // In this situation we want to remove all activities
@@ -2174,7 +2244,7 @@
                         // cases this means we are resetting the task to its
                         // initial state.
                         ActivityRecord top = performClearTaskLocked(
-                                taskTop.task.taskId, r, launchFlags, true);
+                                taskTop.task.taskId, r, launchFlags);
                         if (top != null) {
                             if (top.frontOfTask) {
                                 // Activity aliases may mean we use different
@@ -2235,7 +2305,7 @@
                         // for now we'll just drop it.
                         taskTop.task.setIntent(r.intent, r.info);
                     }
-                    if (!addingToTask) {
+                    if (!addingToTask && reuseTask == null) {
                         // We didn't do anything...  but it was needed (a.k.a., client
                         // don't use that intent!)  And for paranoia, make
                         // sure we have correctly resumed the top activity.
@@ -2298,19 +2368,23 @@
         // Should this be considered a new task?
         if (r.resultTo == null && !addingToTask
                 && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
-            // todo: should do better management of integers.
-            mService.mCurTask++;
-            if (mService.mCurTask <= 0) {
-                mService.mCurTask = 1;
+            if (reuseTask == null) {
+                // todo: should do better management of integers.
+                mService.mCurTask++;
+                if (mService.mCurTask <= 0) {
+                    mService.mCurTask = 1;
+                }
+                r.task = new TaskRecord(mService.mCurTask, r.info, intent);
+                if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+                        + " in new task " + r.task);
+            } else {
+                r.task = reuseTask;
             }
-            r.task = new TaskRecord(mService.mCurTask, r.info, intent,
-                    (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
-            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
-                    + " in new task " + r.task);
             newTask = true;
             if (mMainStack) {
                 mService.addRecentTaskLocked(r.task);
             }
+            moveHomeToFrontFromLaunchLocked(launchFlags);
             
         } else if (sourceRecord != null) {
             if (!addingToTask &&
@@ -2319,7 +2393,7 @@
                 // task, but the caller has asked to clear that task if the
                 // activity is already running.
                 ActivityRecord top = performClearTaskLocked(
-                        sourceRecord.task.taskId, r, launchFlags, true);
+                        sourceRecord.task.taskId, r, launchFlags);
                 if (top != null) {
                     logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
                     top.deliverNewIntentLocked(callingUid, r.intent);
@@ -2361,9 +2435,8 @@
             ActivityRecord prev =
                 N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
             r.task = prev != null
-                ? prev.task
-                : new TaskRecord(mService.mCurTask, r.info, intent,
-                        (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+                    ? prev.task
+                    : new TaskRecord(mService.mCurTask, r.info, intent);
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                     + " in new guessed " + r.task);
         }
@@ -2386,21 +2459,7 @@
         return START_SUCCESS;
     }
 
-    final int startActivityMayWait(IApplicationThread caller,
-            Intent intent, String resolvedType, Uri[] grantedUriPermissions,
-            int grantedMode, IBinder resultTo,
-            String resultWho, int requestCode, boolean onlyIfNeeded,
-            boolean debug, WaitResult outResult, Configuration config) {
-        // Refuse possible leaked file descriptors
-        if (intent != null && intent.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-
-        boolean componentSpecified = intent.getComponent() != null;
-        
-        // Don't modify the client's object!
-        intent = new Intent(intent);
-
+    ActivityInfo resolveActivity(Intent intent, String resolvedType, boolean debug) {
         // Collect information about the target of the Intent.
         ActivityInfo aInfo;
         try {
@@ -2429,11 +2488,32 @@
                 }
             }
         }
+        return aInfo;
+    }
+
+    final int startActivityMayWait(IApplicationThread caller, int callingUid,
+            Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+            int grantedMode, IBinder resultTo,
+            String resultWho, int requestCode, boolean onlyIfNeeded,
+            boolean debug, WaitResult outResult, Configuration config) {
+        // Refuse possible leaked file descriptors
+        if (intent != null && intent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+
+        boolean componentSpecified = intent.getComponent() != null;
+
+        // Don't modify the client's object!
+        intent = new Intent(intent);
+
+        // Collect information about the target of the Intent.
+        ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug);
 
         synchronized (mService) {
             int callingPid;
-            int callingUid;
-            if (caller == null) {
+            if (callingUid >= 0) {
+                callingPid = -1;
+            } else if (caller == null) {
                 callingPid = Binder.getCallingPid();
                 callingUid = Binder.getCallingUid();
             } else {
@@ -2472,8 +2552,8 @@
                         
                         IIntentSender target = mService.getIntentSenderLocked(
                                 IActivityManager.INTENT_SENDER_ACTIVITY, "android",
-                                realCallingUid, null, null, 0, intent,
-                                resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
+                                realCallingUid, null, null, 0, new Intent[] { intent },
+                                new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
                                 | PendingIntent.FLAG_ONE_SHOT);
                         
                         Intent newIntent = new Intent();
@@ -2518,7 +2598,7 @@
             int res = startActivityLocked(caller, intent, resolvedType,
                     grantedUriPermissions, grantedMode, aInfo,
                     resultTo, resultWho, requestCode, callingPid, callingUid,
-                    onlyIfNeeded, componentSpecified);
+                    onlyIfNeeded, componentSpecified, null);
             
             if (mConfigWillChange && mMainStack) {
                 // If the caller also wants to switch to a new configuration,
@@ -2569,6 +2649,75 @@
         }
     }
     
+    final int startActivities(IApplicationThread caller, int callingUid,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+        if (intents == null) {
+            throw new NullPointerException("intents is null");
+        }
+        if (resolvedTypes == null) {
+            throw new NullPointerException("resolvedTypes is null");
+        }
+        if (intents.length != resolvedTypes.length) {
+            throw new IllegalArgumentException("intents are length different than resolvedTypes");
+        }
+
+        ActivityRecord[] outActivity = new ActivityRecord[1];
+
+        int callingPid;
+        if (callingUid >= 0) {
+            callingPid = -1;
+        } else if (caller == null) {
+            callingPid = Binder.getCallingPid();
+            callingUid = Binder.getCallingUid();
+        } else {
+            callingPid = callingUid = -1;
+        }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mService) {
+
+                for (int i=0; i<intents.length; i++) {
+                    Intent intent = intents[i];
+                    if (intent == null) {
+                        continue;
+                    }
+
+                    // Refuse possible leaked file descriptors
+                    if (intent != null && intent.hasFileDescriptors()) {
+                        throw new IllegalArgumentException("File descriptors passed in Intent");
+                    }
+
+                    boolean componentSpecified = intent.getComponent() != null;
+
+                    // Don't modify the client's object!
+                    intent = new Intent(intent);
+
+                    // Collect information about the target of the Intent.
+                    ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i], false);
+
+                    if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
+                            & ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+                        throw new IllegalArgumentException(
+                                "FLAG_CANT_SAVE_STATE not supported here");
+                    }
+
+                    int res = startActivityLocked(caller, intent, resolvedTypes[i],
+                            null, 0, aInfo, resultTo, null, -1, callingPid, callingUid,
+                            false, componentSpecified, outActivity);
+                    if (res < 0) {
+                        return res;
+                    }
+
+                    resultTo = outActivity[0];
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        return IActivityManager.START_SUCCESS;
+    }
+
     void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
             long thisTime, long totalTime) {
         for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
@@ -3252,6 +3401,24 @@
         removeHistoryRecordsForAppLocked(mFinishingActivities, app);
     }
     
+    /**
+     * Move the current home activity's task (if one exists) to the front
+     * of the stack.
+     */
+    final void moveHomeToFrontLocked() {
+        TaskRecord homeTask = null;
+        for (int i=mHistory.size()-1; i>=0; i--) {
+            ActivityRecord hr = (ActivityRecord)mHistory.get(i);
+            if (hr.isHomeActivity) {
+                homeTask = hr.task;
+            }
+        }
+        if (homeTask != null) {
+            moveTaskToFrontLocked(homeTask, null);
+        }
+    }
+
+
     final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
         if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
 
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 7a85eb8..ee6e420 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -47,20 +47,24 @@
         final int requestCode;
         final Intent requestIntent;
         final String requestResolvedType;
+        Intent[] allIntents;
+        String[] allResolvedTypes;
         final int flags;
         final int hashCode;
         
         private static final int ODD_PRIME_NUMBER = 37;
         
         Key(int _t, String _p, ActivityRecord _a, String _w,
-                int _r, Intent _i, String _it, int _f) {
+                int _r, Intent[] _i, String[] _it, int _f) {
             type = _t;
             packageName = _p;
             activity = _a;
             who = _w;
             requestCode = _r;
-            requestIntent = _i;
-            requestResolvedType = _it;
+            requestIntent = _i != null ? _i[_i.length-1] : null;
+            requestResolvedType = _it != null ? _it[_it.length-1] : null;
+            allIntents = _i;
+            allResolvedTypes = _it;
             flags = _f;
             
             int hash = 23;
@@ -72,11 +76,11 @@
             if (_a != null) {
                 hash = (ODD_PRIME_NUMBER*hash) + _a.hashCode();
             }
-            if (_i != null) {
-                hash = (ODD_PRIME_NUMBER*hash) + _i.filterHashCode();
+            if (requestIntent != null) {
+                hash = (ODD_PRIME_NUMBER*hash) + requestIntent.filterHashCode();
             }
-            if (_it != null) {
-                hash = (ODD_PRIME_NUMBER*hash) + _it.hashCode();
+            if (requestResolvedType != null) {
+                hash = (ODD_PRIME_NUMBER*hash) + requestResolvedType.hashCode();
             }
             hash = (ODD_PRIME_NUMBER*hash) + _p.hashCode();
             hash = (ODD_PRIME_NUMBER*hash) + _t;
@@ -209,9 +213,24 @@
                 switch (key.type) {
                     case IActivityManager.INTENT_SENDER_ACTIVITY:
                         try {
-                            owner.startActivityInPackage(uid,
-                                    finalIntent, resolvedType,
-                                    resultTo, resultWho, requestCode, false);
+                            if (key.allIntents != null && key.allIntents.length > 1) {
+                                Intent[] allIntents = new Intent[key.allIntents.length];
+                                String[] allResolvedTypes = new String[key.allIntents.length];
+                                System.arraycopy(key.allIntents, 0, allIntents, 0,
+                                        key.allIntents.length);
+                                if (key.allResolvedTypes != null) {
+                                    System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0,
+                                            key.allResolvedTypes.length);
+                                }
+                                allIntents[allIntents.length-1] = finalIntent;
+                                allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
+                                owner.startActivitiesInPackage(uid, allIntents,
+                                        allResolvedTypes, resultTo);
+                            } else {
+                                owner.startActivityInPackage(uid,
+                                        finalIntent, resolvedType,
+                                        resultTo, resultWho, requestCode, false);
+                            }
                         } catch (RuntimeException e) {
                             Slog.w(ActivityManagerService.TAG,
                                     "Unable to send startActivity intent", e);
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index bcb8f54..09d9c3b6 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -26,7 +26,6 @@
 class TaskRecord {
     final int taskId;       // Unique identifier for this task.
     final String affinity;  // The affinity name for this task, or null.
-    final boolean clearOnBackground; // As per the original activity.
     Intent intent;          // The original intent that started the task.
     Intent affinityIntent;  // Intent of affinity-moved activity that started this task.
     ComponentName origActivity; // The non-alias activity component of the intent.
@@ -38,11 +37,9 @@
 
     String stringName;      // caching of toString() result.
     
-    TaskRecord(int _taskId, ActivityInfo info, Intent _intent,
-            boolean _clearOnBackground) {
+    TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
         taskId = _taskId;
         affinity = info.taskAffinity;
-        clearOnBackground = _clearOnBackground;
         setIntent(_intent, info);
     }
 
@@ -86,9 +83,8 @@
     }
     
     void dump(PrintWriter pw, String prefix) {
-        if (clearOnBackground || numActivities != 0 || rootWasReset) {
-            pw.print(prefix); pw.print("clearOnBackground="); pw.print(clearOnBackground);
-                    pw.print(" numActivities="); pw.print(numActivities);
+        if (numActivities != 0 || rootWasReset) {
+            pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
                     pw.print(" rootWasReset="); pw.println(rootWasReset);
         }
         if (affinity != null) {
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
new file mode 100644
index 0000000..3543275
--- /dev/null
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.gsm.SmsCbHeader;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Describes an SMS-CB message.
+ *
+ * {@hide}
+ */
+public class SmsCbMessage {
+
+    /**
+     * Cell wide immediate geographical scope
+     */
+    public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
+
+    /**
+     * PLMN wide geographical scope
+     */
+    public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1;
+
+    /**
+     * Location / service area wide geographical scope
+     */
+    public static final int GEOGRAPHICAL_SCOPE_LA_WIDE = 2;
+
+    /**
+     * Cell wide geographical scope
+     */
+    public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3;
+
+    /**
+     * Create an instance of this class from a received PDU
+     *
+     * @param pdu PDU bytes
+     * @return An instance of this class, or null if invalid pdu
+     */
+    public static SmsCbMessage createFromPdu(byte[] pdu) {
+        try {
+            return new SmsCbMessage(pdu);
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
+     */
+    private static final String[] LANGUAGE_CODES_GROUP_0 = {
+            "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu",
+            "pl", null
+    };
+
+    /**
+     * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
+     */
+    private static final String[] LANGUAGE_CODES_GROUP_2 = {
+            "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null,
+            null, null
+    };
+
+    private static final char CARRIAGE_RETURN = 0x0d;
+
+    private SmsCbHeader mHeader;
+
+    private String mLanguage;
+
+    private String mBody;
+
+    private SmsCbMessage(byte[] pdu) throws IllegalArgumentException {
+        mHeader = new SmsCbHeader(pdu);
+        parseBody(pdu);
+    }
+
+    /**
+     * Return the geographical scope of this message, one of
+     * {@link #GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE},
+     * {@link #GEOGRAPHICAL_SCOPE_PLMN_WIDE},
+     * {@link #GEOGRAPHICAL_SCOPE_LA_WIDE},
+     * {@link #GEOGRAPHICAL_SCOPE_CELL_WIDE}
+     *
+     * @return Geographical scope
+     */
+    public int getGeographicalScope() {
+        return mHeader.geographicalScope;
+    }
+
+    /**
+     * Get the ISO-639-1 language code for this message, or null if unspecified
+     *
+     * @return Language code
+     */
+    public String getLanguageCode() {
+        return mLanguage;
+    }
+
+    /**
+     * Get the body of this message, or null if no body available
+     *
+     * @return Body, or null
+     */
+    public String getMessageBody() {
+        return mBody;
+    }
+
+    /**
+     * Get the message identifier of this message (0-65535)
+     *
+     * @return Message identifier
+     */
+    public int getMessageIdentifier() {
+        return mHeader.messageIdentifier;
+    }
+
+    /**
+     * Get the message code of this message (0-1023)
+     *
+     * @return Message code
+     */
+    public int getMessageCode() {
+        return mHeader.messageCode;
+    }
+
+    /**
+     * Get the update number of this message (0-15)
+     *
+     * @return Update number
+     */
+    public int getUpdateNumber() {
+        return mHeader.updateNumber;
+    }
+
+    private void parseBody(byte[] pdu) {
+        int encoding;
+        boolean hasLanguageIndicator = false;
+
+        // Extract encoding and language from DCS, as defined in 3gpp TS 23.038,
+        // section 5.
+        switch ((mHeader.dataCodingScheme & 0xf0) >> 4) {
+            case 0x00:
+                encoding = SmsMessage.ENCODING_7BIT;
+                mLanguage = LANGUAGE_CODES_GROUP_0[mHeader.dataCodingScheme & 0x0f];
+                break;
+
+            case 0x01:
+                hasLanguageIndicator = true;
+                if ((mHeader.dataCodingScheme & 0x0f) == 0x01) {
+                    encoding = SmsMessage.ENCODING_16BIT;
+                } else {
+                    encoding = SmsMessage.ENCODING_7BIT;
+                }
+                break;
+
+            case 0x02:
+                encoding = SmsMessage.ENCODING_7BIT;
+                mLanguage = LANGUAGE_CODES_GROUP_2[mHeader.dataCodingScheme & 0x0f];
+                break;
+
+            case 0x03:
+                encoding = SmsMessage.ENCODING_7BIT;
+                break;
+
+            case 0x04:
+            case 0x05:
+                switch ((mHeader.dataCodingScheme & 0x0c) >> 2) {
+                    case 0x01:
+                        encoding = SmsMessage.ENCODING_8BIT;
+                        break;
+
+                    case 0x02:
+                        encoding = SmsMessage.ENCODING_16BIT;
+                        break;
+
+                    case 0x00:
+                    default:
+                        encoding = SmsMessage.ENCODING_7BIT;
+                        break;
+                }
+                break;
+
+            case 0x06:
+            case 0x07:
+                // Compression not supported
+            case 0x09:
+                // UDH structure not supported
+            case 0x0e:
+                // Defined by the WAP forum not supported
+                encoding = SmsMessage.ENCODING_UNKNOWN;
+                break;
+
+            case 0x0f:
+                if (((mHeader.dataCodingScheme & 0x04) >> 2) == 0x01) {
+                    encoding = SmsMessage.ENCODING_8BIT;
+                } else {
+                    encoding = SmsMessage.ENCODING_7BIT;
+                }
+                break;
+
+            default:
+                // Reserved values are to be treated as 7-bit
+                encoding = SmsMessage.ENCODING_7BIT;
+                break;
+        }
+
+        switch (encoding) {
+            case SmsMessage.ENCODING_7BIT:
+                mBody = GsmAlphabet.gsm7BitPackedToString(pdu, SmsCbHeader.PDU_HEADER_LENGTH,
+                        (pdu.length - SmsCbHeader.PDU_HEADER_LENGTH) * 8 / 7);
+
+                if (hasLanguageIndicator && mBody != null && mBody.length() > 2) {
+                    mLanguage = mBody.substring(0, 2);
+                    mBody = mBody.substring(3);
+                }
+                break;
+
+            case SmsMessage.ENCODING_16BIT:
+                int offset = SmsCbHeader.PDU_HEADER_LENGTH;
+
+                if (hasLanguageIndicator && pdu.length >= SmsCbHeader.PDU_HEADER_LENGTH + 2) {
+                    mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu,
+                            SmsCbHeader.PDU_HEADER_LENGTH, 2);
+                    offset += 2;
+                }
+
+                try {
+                    mBody = new String(pdu, offset, (pdu.length & 0xfffe) - offset, "utf-16");
+                } catch (UnsupportedEncodingException e) {
+                    // Eeeek
+                }
+                break;
+
+            default:
+                break;
+        }
+
+        if (mBody != null) {
+            // Remove trailing carriage return
+            for (int i = mBody.length() - 1; i >= 0; i--) {
+                if (mBody.charAt(i) != CARRIAGE_RETURN) {
+                    mBody = mBody.substring(0, i + 1);
+                    break;
+                }
+            }
+        } else {
+            mBody = "";
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 953696b..6a346af 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -338,7 +338,67 @@
         }
 
         return createMessageListFromRawRecords(records);
-   }
+    }
+
+    /**
+     * Enable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier. Note that if two different clients enable the same
+     * message identifier, they must both disable it for the device to stop
+     * receiving those messages. All received messages will be broadcast in an
+     * intent with the action "android.provider.telephony.SMS_CB_RECEIVED".
+     * Note: This call is blocking, callers may want to avoid calling it from
+     * the main thread of an application.
+     *
+     * @param messageIdentifier Message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     * @see #disableCellBroadcast(int)
+     *
+     * {@hide}
+     */
+    public boolean enableCellBroadcast(int messageIdentifier) {
+        boolean success = false;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.enableCellBroadcast(messageIdentifier);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Disable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier. Note that if two different clients enable the same
+     * message identifier, they must both disable it for the device to stop
+     * receiving those messages.
+     * Note: This call is blocking, callers may want to avoid calling it from
+     * the main thread of an application.
+     *
+     * @param messageIdentifier Message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     *
+     * @see #enableCellBroadcast(int)
+     *
+     * {@hide}
+     */
+    public boolean disableCellBroadcast(int messageIdentifier) {
+        boolean success = false;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.disableCellBroadcast(messageIdentifier);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
 
     /**
      * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 96c90a2..9c738ec 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -149,8 +149,6 @@
 
     /** Slow poll when attempting connection recovery. */
     protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000;
-    /** Default ping deadline, in seconds. */
-    protected static final int DEFAULT_PING_DEADLINE = 5;
     /** Default max failure count before attempting to network re-registration. */
     protected static final int DEFAULT_MAX_PDP_RESET_FAIL = 3;
 
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 65bad96..90de5e1 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -144,4 +144,30 @@
             in List<String> parts, in List<PendingIntent> sentIntents,
             in List<PendingIntent> deliveryIntents);
 
+    /**
+     * Enable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier. Note that if two different clients enable the same
+     * message identifier, they must both disable it for the device to stop
+     * receiving those messages.
+     *
+     * @param messageIdentifier Message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     *
+     * @see #disableCellBroadcast(int)
+     */
+    boolean enableCellBroadcast(int messageIdentifier);
+
+    /**
+     * Disable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier. Note that if two different clients enable the same
+     * message identifier, they must both disable it for the device to stop
+     * receiving those messages.
+     *
+     * @param messageIdentifier Message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     *
+     * @see #enableCellBroadcast(int)
+     */
+    boolean disableCellBroadcast(int messageIdentifier);
+
 }
diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
index 1910a9c..5049249 100644
--- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
+++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
@@ -68,4 +68,12 @@
                 parts, sentIntents, deliveryIntents);
     }
 
+    public boolean enableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
+        return mIccSmsInterfaceManager.enableCellBroadcast(messageIdentifier);
+    }
+
+    public boolean disableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
+        return mIccSmsInterfaceManager.disableCellBroadcast(messageIdentifier);
+    }
+
 }
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 3a7ce47..ec49a19 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -112,6 +112,9 @@
     /** Radio is ON */
     static final protected int EVENT_RADIO_ON = 12;
 
+    /** New broadcast SMS */
+    static final protected int EVENT_NEW_BROADCAST_SMS = 13;
+
     protected Phone mPhone;
     protected Context mContext;
     protected ContentResolver mResolver;
@@ -390,6 +393,9 @@
                 mCm.reportSmsMemoryStatus(mStorageAvailable,
                         obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
             }
+
+        case EVENT_NEW_BROADCAST_SMS:
+            handleBroadcastSms((AsyncResult)msg.obj);
             break;
         }
     }
@@ -985,4 +991,17 @@
             }
         }
     };
+
+    protected abstract void handleBroadcastSms(AsyncResult ar);
+
+    protected void dispatchBroadcastPdus(byte[][] pdus) {
+        Intent intent = new Intent("android.provider.telephony.SMS_CB_RECEIVED");
+        intent.putExtra("pdus", pdus);
+
+        if (Config.LOGD)
+            Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus");
+
+        dispatch(intent, "android.permission.RECEIVE_SMS");
+    }
+
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 37a33bc..53555d8 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -494,6 +494,11 @@
         mCm.setCdmaBroadcastConfig(configValuesArray, response);
     }
 
+    protected void handleBroadcastSms(AsyncResult ar) {
+        // Not supported
+        Log.e(TAG, "Error! Not implemented for CDMA.");
+    }
+
     private int resultToCause(int rc) {
         switch (rc) {
         case Activity.RESULT_OK:
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index d84b6ab..29f3bc1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -191,6 +191,18 @@
         return mSms;
     }
 
+    public boolean enableCellBroadcast(int messageIdentifier) {
+        // Not implemented
+        Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+        return false;
+    }
+
+    public boolean disableCellBroadcast(int messageIdentifier) {
+        // Not implemented
+        Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+        return false;
+    }
+
     protected void log(String msg) {
         Log.d(LOG_TAG, "[RuimSmsInterfaceManager] " + msg);
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 06c7c1b..bbfdd31 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -81,7 +81,6 @@
     private boolean mReregisterOnReconnectFailure = false;
     private ContentResolver mResolver;
 
-    private boolean mPingTestActive = false;
     // Count of PDP reset attempts; reset when we see incoming,
     // call reRegisterNetwork, or pingTest succeeds.
     private int mPdpResetCount = 0;
@@ -654,7 +653,7 @@
 
     @Override
     protected void startNetStatPoll() {
-        if (mState == State.CONNECTED && mPingTestActive == false && mNetStatPollEnabled == false) {
+        if (mState == State.CONNECTED && mNetStatPollEnabled == false) {
             log("[DataConnection] Start poll NetStat");
             resetPollStats();
             mNetStatPollEnabled = true;
@@ -763,18 +762,9 @@
                             POLL_NETSTAT_SLOW_MILLIS);
                 } else {
                     if (DBG) log("Sent " + String.valueOf(mSentSinceLastRecv) +
-                                        " pkts since last received");
-                    // We've exceeded the threshold.  Run ping test as a final check;
-                    // it will proceed with recovery if ping fails.
+                                        " pkts since last received start recovery process");
                     stopNetStatPoll();
-                    Thread pingTest = new Thread() {
-                        @Override
-                        public void run() {
-                            runPingTest();
-                        }
-                    };
-                    mPingTestActive = true;
-                    pingTest.start();
+                    sendMessage(obtainMessage(EVENT_START_RECOVERY));
                 }
             } else {
                 mNoRecvPollCount = 0;
@@ -794,37 +784,6 @@
         }
     };
 
-    private void runPingTest () {
-        int status = -1;
-        try {
-            String address = Settings.Secure.getString(mResolver,
-                    Settings.Secure.PDP_WATCHDOG_PING_ADDRESS);
-            int deadline = Settings.Secure.getInt(mResolver,
-                        Settings.Secure.PDP_WATCHDOG_PING_DEADLINE, DEFAULT_PING_DEADLINE);
-            if (DBG) log("pinging " + address + " for " + deadline + "s");
-            if (address != null && !NULL_IP.equals(address)) {
-                Process p = Runtime.getRuntime()
-                                .exec("ping -c 1 -i 1 -w "+ deadline + " " + address);
-                status = p.waitFor();
-            }
-        } catch (IOException e) {
-            loge("ping failed: IOException");
-        } catch (Exception e) {
-            loge("exception trying to ping");
-        }
-
-        if (status == 0) {
-            // ping succeeded.  False alarm.  Reset netStatPoll.
-            // ("-1" for this event indicates a false alarm)
-            EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, -1);
-            mPdpResetCount = 0;
-            sendMessage(obtainMessage(EVENT_START_NETSTAT_POLL));
-        } else {
-            // ping failed.  Proceed with recovery.
-            sendMessage(obtainMessage(EVENT_START_RECOVERY));
-        }
-    }
-
     /**
      * Returns true if the last fail cause is something that
      * seems like it deserves an error notification.
@@ -1399,12 +1358,10 @@
                 break;
 
             case EVENT_START_NETSTAT_POLL:
-                mPingTestActive = false;
                 startNetStatPoll();
                 break;
 
             case EVENT_START_RECOVERY:
-                mPingTestActive = false;
                 doRecovery();
                 break;
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index ed7066b..70f8f86 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -22,20 +22,26 @@
 import android.content.Intent;
 import android.os.AsyncResult;
 import android.os.Message;
+import android.os.SystemProperties;
 import android.provider.Telephony.Sms.Intents;
 import android.telephony.ServiceState;
+import android.telephony.SmsCbMessage;
+import android.telephony.gsm.GsmCellLocation;
 import android.util.Config;
 import android.util.Log;
 
+import com.android.internal.telephony.BaseCommands;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
+import com.android.internal.telephony.TelephonyProperties;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 
 import static android.telephony.SmsMessage.MessageClass;
 
@@ -47,6 +53,8 @@
     GsmSMSDispatcher(GSMPhone phone) {
         super(phone);
         mGsmPhone = phone;
+
+        ((BaseCommands)mCm).setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null);
     }
 
     /**
@@ -394,4 +402,162 @@
                 return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR;
         }
     }
+
+    /**
+     * Holds all info about a message page needed to assemble a complete
+     * concatenated message
+     */
+    private static final class SmsCbConcatInfo {
+        private final SmsCbHeader mHeader;
+
+        private final String mPlmn;
+
+        private final int mLac;
+
+        private final int mCid;
+
+        public SmsCbConcatInfo(SmsCbHeader header, String plmn, int lac, int cid) {
+            mHeader = header;
+            mPlmn = plmn;
+            mLac = lac;
+            mCid = cid;
+        }
+
+        @Override
+        public int hashCode() {
+            return mHeader.messageIdentifier * 31 + mHeader.updateNumber;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof SmsCbConcatInfo) {
+                SmsCbConcatInfo other = (SmsCbConcatInfo)obj;
+
+                // Two pages match if all header attributes (except the page
+                // index) are identical, and both pages belong to the same
+                // location (which is also determined by the scope parameter)
+                if (mHeader.geographicalScope == other.mHeader.geographicalScope
+                        && mHeader.messageCode == other.mHeader.messageCode
+                        && mHeader.updateNumber == other.mHeader.updateNumber
+                        && mHeader.messageIdentifier == other.mHeader.messageIdentifier
+                        && mHeader.dataCodingScheme == other.mHeader.dataCodingScheme
+                        && mHeader.nrOfPages == other.mHeader.nrOfPages) {
+                    return matchesLocation(other.mPlmn, other.mLac, other.mCid);
+                }
+            }
+
+            return false;
+        }
+
+        /**
+         * Checks if this concatenation info matches the given location. The
+         * granularity of the match depends on the geographical scope.
+         *
+         * @param plmn PLMN
+         * @param lac Location area code
+         * @param cid Cell ID
+         * @return true if matching, false otherwise
+         */
+        public boolean matchesLocation(String plmn, int lac, int cid) {
+            switch (mHeader.geographicalScope) {
+                case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE:
+                case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE:
+                    if (mCid != cid) {
+                        return false;
+                    }
+                    // deliberate fall-through
+                case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE:
+                    if (mLac != lac) {
+                        return false;
+                    }
+                    // deliberate fall-through
+                case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE:
+                    return mPlmn != null && mPlmn.equals(plmn);
+            }
+
+            return false;
+        }
+    }
+
+    // This map holds incomplete concatenated messages waiting for assembly
+    private HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap =
+            new HashMap<SmsCbConcatInfo, byte[][]>();
+
+    protected void handleBroadcastSms(AsyncResult ar) {
+        try {
+            byte[][] pdus = null;
+            byte[] receivedPdu = (byte[])ar.result;
+
+            if (Config.LOGD) {
+                for (int i = 0; i < receivedPdu.length; i += 8) {
+                    StringBuilder sb = new StringBuilder("SMS CB pdu data: ");
+                    for (int j = i; j < i + 8 && j < receivedPdu.length; j++) {
+                        int b = receivedPdu[j] & 0xff;
+                        if (b < 0x10) {
+                            sb.append("0");
+                        }
+                        sb.append(Integer.toHexString(b)).append(" ");
+                    }
+                    Log.d(TAG, sb.toString());
+                }
+            }
+
+            SmsCbHeader header = new SmsCbHeader(receivedPdu);
+            String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC);
+            GsmCellLocation cellLocation = (GsmCellLocation)mGsmPhone.getCellLocation();
+            int lac = cellLocation.getLac();
+            int cid = cellLocation.getCid();
+
+            if (header.nrOfPages > 1) {
+                // Multi-page message
+                SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, plmn, lac, cid);
+
+                // Try to find other pages of the same message
+                pdus = mSmsCbPageMap.get(concatInfo);
+
+                if (pdus == null) {
+                    // This it the first page of this message, make room for all
+                    // pages and keep until complete
+                    pdus = new byte[header.nrOfPages][];
+
+                    mSmsCbPageMap.put(concatInfo, pdus);
+                }
+
+                // Page parameter is one-based
+                pdus[header.pageIndex - 1] = receivedPdu;
+
+                for (int i = 0; i < pdus.length; i++) {
+                    if (pdus[i] == null) {
+                        // Still missing pages, exit
+                        return;
+                    }
+                }
+
+                // Message complete, remove and dispatch
+                mSmsCbPageMap.remove(concatInfo);
+            } else {
+                // Single page message
+                pdus = new byte[1][];
+                pdus[0] = receivedPdu;
+            }
+
+            dispatchBroadcastPdus(pdus);
+
+            // Remove messages that are out of scope to prevent the map from
+            // growing indefinitely, containing incomplete messages that were
+            // never assembled
+            Iterator<SmsCbConcatInfo> iter = mSmsCbPageMap.keySet().iterator();
+
+            while (iter.hasNext()) {
+                SmsCbConcatInfo info = iter.next();
+
+                if (!info.matchesLocation(plmn, lac, cid)) {
+                    iter.remove();
+                }
+            }
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Error in decoding SMS CB pdu", e);
+        }
+    }
+
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index aab359f..5abba6e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -17,7 +17,9 @@
 package com.android.internal.telephony.gsm;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.AsyncResult;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
 import android.util.Log;
@@ -30,7 +32,10 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
 
@@ -45,9 +50,15 @@
     private final Object mLock = new Object();
     private boolean mSuccess;
     private List<SmsRawData> mSms;
+    private HashMap<Integer, HashSet<String>> mCellBroadcastSubscriptions =
+            new HashMap<Integer, HashSet<String>>();
 
     private static final int EVENT_LOAD_DONE = 1;
     private static final int EVENT_UPDATE_DONE = 2;
+    private static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3;
+    private static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4;
+    private static final int SMS_CB_CODE_SCHEME_MIN = 0;
+    private static final int SMS_CB_CODE_SCHEME_MAX = 255;
 
     Handler mHandler = new Handler() {
         @Override
@@ -75,6 +86,14 @@
                         mLock.notifyAll();
                     }
                     break;
+                case EVENT_SET_BROADCAST_ACTIVATION_DONE:
+                case EVENT_SET_BROADCAST_CONFIG_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    synchronized (mLock) {
+                        mSuccess = (ar.exception == null);
+                        mLock.notifyAll();
+                    }
+                    break;
             }
         }
     };
@@ -193,6 +212,126 @@
         return mSms;
     }
 
+    public boolean enableCellBroadcast(int messageIdentifier) {
+        if (DBG) log("enableCellBroadcast");
+
+        Context context = mPhone.getContext();
+
+        context.enforceCallingPermission(
+                "android.permission.RECEIVE_SMS",
+                "Enabling cell broadcast SMS");
+
+        String client = context.getPackageManager().getNameForUid(
+                Binder.getCallingUid());
+        HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier);
+
+        if (clients == null) {
+            // This is a new message identifier
+            clients = new HashSet<String>();
+            mCellBroadcastSubscriptions.put(messageIdentifier, clients);
+
+            if (!updateCellBroadcastConfig()) {
+                mCellBroadcastSubscriptions.remove(messageIdentifier);
+                return false;
+            }
+        }
+
+        clients.add(client);
+
+        if (DBG)
+            log("Added cell broadcast subscription for MID " + messageIdentifier
+                    + " from client " + client);
+
+        return true;
+    }
+
+    public boolean disableCellBroadcast(int messageIdentifier) {
+        if (DBG) log("disableCellBroadcast");
+
+        Context context = mPhone.getContext();
+
+        context.enforceCallingPermission(
+                "android.permission.RECEIVE_SMS",
+                "Disabling cell broadcast SMS");
+
+        String client = context.getPackageManager().getNameForUid(
+                Binder.getCallingUid());
+        HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier);
+
+        if (clients != null && clients.remove(client)) {
+            if (DBG)
+                log("Removed cell broadcast subscription for MID " + messageIdentifier
+                        + " from client " + client);
+
+            if (clients.isEmpty()) {
+                mCellBroadcastSubscriptions.remove(messageIdentifier);
+                updateCellBroadcastConfig();
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    private boolean updateCellBroadcastConfig() {
+        Set<Integer> messageIdentifiers = mCellBroadcastSubscriptions.keySet();
+
+        if (messageIdentifiers.size() > 0) {
+            SmsBroadcastConfigInfo[] configs =
+                    new SmsBroadcastConfigInfo[messageIdentifiers.size()];
+            int i = 0;
+
+            for (int messageIdentifier : messageIdentifiers) {
+                configs[i++] = new SmsBroadcastConfigInfo(messageIdentifier, messageIdentifier,
+                        SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, true);
+            }
+
+            return setCellBroadcastConfig(configs) && setCellBroadcastActivation(true);
+        } else {
+            return setCellBroadcastActivation(false);
+        }
+    }
+
+    private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) {
+        if (DBG)
+            log("Calling setGsmBroadcastConfig with " + configs.length + " configurations");
+
+        synchronized (mLock) {
+            Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
+
+            mSuccess = false;
+            mPhone.mCM.setGsmBroadcastConfig(configs, response);
+
+            try {
+                mLock.wait();
+            } catch (InterruptedException e) {
+                log("interrupted while trying to set cell broadcast config");
+            }
+        }
+
+        return mSuccess;
+    }
+
+    private boolean setCellBroadcastActivation(boolean activate) {
+        if (DBG)
+            log("Calling setCellBroadcastActivation(" + activate + ")");
+
+        synchronized (mLock) {
+            Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
+
+            mSuccess = false;
+            mPhone.mCM.setGsmBroadcastActivation(activate, response);
+
+            try {
+                mLock.wait();
+            } catch (InterruptedException e) {
+                log("interrupted while trying to set cell broadcast activation");
+            }
+        }
+
+        return mSuccess;
+    }
+
     @Override
     protected void log(String msg) {
         Log.d(LOG_TAG, "[SimSmsInterfaceManager] " + msg);
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
new file mode 100644
index 0000000..5f27cfc
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.gsm;
+
+public class SmsCbHeader {
+    public static final int PDU_HEADER_LENGTH = 6;
+
+    public final int geographicalScope;
+
+    public final int messageCode;
+
+    public final int updateNumber;
+
+    public final int messageIdentifier;
+
+    public final int dataCodingScheme;
+
+    public final int pageIndex;
+
+    public final int nrOfPages;
+
+    public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
+        if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
+            throw new IllegalArgumentException("Illegal PDU");
+        }
+
+        geographicalScope = (pdu[0] & 0xc0) >> 6;
+        messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
+        updateNumber = pdu[1] & 0x0f;
+        messageIdentifier = (pdu[2] << 8) | pdu[3];
+        dataCodingScheme = pdu[4];
+
+        // Check for invalid page parameter
+        int pageIndex = (pdu[5] & 0xf0) >> 4;
+        int nrOfPages = pdu[5] & 0x0f;
+
+        if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
+            pageIndex = 1;
+            nrOfPages = 1;
+        }
+
+        this.pageIndex = pageIndex;
+        this.nrOfPages = nrOfPages;
+    }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
new file mode 100644
index 0000000..7136ea0
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.telephony.SmsCbMessage;
+import android.test.AndroidTestCase;
+
+/**
+ * Test cases for basic SmsCbMessage operations
+ */
+public class GsmSmsCbTest extends AndroidTestCase {
+
+    private void doTestGeographicalScopeValue(byte[] pdu, byte b, int expectedGs) {
+        pdu[0] = b;
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected geographical scope decoded", expectedGs, msg
+                .getGeographicalScope());
+    }
+
+    public void testCreateNullPdu() {
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(null);
+
+        assertNull("createFromPdu(byte[] with null pdu should return null", msg);
+    }
+
+    public void testCreateTooShortPdu() {
+        byte[] pdu = new byte[4];
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertNull("createFromPdu(byte[] with too short pdu should return null", msg);
+    }
+
+    public void testGetGeographicalScope() {
+        byte[] pdu = {
+                (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+                (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+                (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+                (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+                (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+                (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+                (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+                (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+        };
+
+        doTestGeographicalScopeValue(pdu, (byte)0x00,
+                SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE);
+        doTestGeographicalScopeValue(pdu, (byte)0x40, SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE);
+        doTestGeographicalScopeValue(pdu, (byte)0x80, SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE);
+        doTestGeographicalScopeValue(pdu, (byte)0xC0, SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE);
+    }
+
+    public void testGetMessageBody7Bit() {
+        byte[] pdu = {
+                (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+                (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+                (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+                (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+                (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+                (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+                (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+                (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A GSM default alphabet message with carriage return padding",
+                msg.getMessageBody());
+    }
+
+    public void testGetMessageBody7BitFull() {
+        byte[] pdu = {
+                (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+                (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+                (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+                (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+                (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xC4, (byte)0xE5,
+                (byte)0xB4, (byte)0xFB, (byte)0x0C, (byte)0x2A, (byte)0xE3, (byte)0xC3, (byte)0x63,
+                (byte)0x3A, (byte)0x3B, (byte)0x0F, (byte)0xCA, (byte)0xCD, (byte)0x40, (byte)0x63,
+                (byte)0x74, (byte)0x58, (byte)0x1E, (byte)0x1E, (byte)0xD3, (byte)0xCB, (byte)0xF2,
+                (byte)0x39, (byte)0x88, (byte)0xFD, (byte)0x76, (byte)0x9F, (byte)0x59, (byte)0xA0,
+                (byte)0x76, (byte)0x39, (byte)0xEC, (byte)0x4E, (byte)0xBB, (byte)0xCF, (byte)0x20,
+                (byte)0x3A, (byte)0xBA, (byte)0x2C, (byte)0x2F, (byte)0x83, (byte)0xD2, (byte)0x73,
+                (byte)0x90, (byte)0xFB, (byte)0x0D, (byte)0x82, (byte)0x87, (byte)0xC9, (byte)0xE4,
+                (byte)0xB4, (byte)0xFB, (byte)0x1C, (byte)0x02
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals(
+                "Unexpected 7-bit string decoded",
+                "A GSM default alphabet message being exactly 93 characters long, " +
+                "meaning there is no padding!",
+                msg.getMessageBody());
+    }
+
+    public void testGetMessageBody7BitWithLanguage() {
+        byte[] pdu = {
+                (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x04, (byte)0x11, (byte)0x41,
+                (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+                (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+                (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+                (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+                (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+                (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+                (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A GSM default alphabet message with carriage return padding",
+                msg.getMessageBody());
+
+        assertEquals("Unexpected language indicator decoded", "es", msg.getLanguageCode());
+    }
+
+    public void testGetMessageBody7BitWithLanguageInBody() {
+        byte[] pdu = {
+                (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x10, (byte)0x11, (byte)0x73,
+                (byte)0x7B, (byte)0x23, (byte)0x08, (byte)0x3A, (byte)0x4E, (byte)0x9B, (byte)0x20,
+                (byte)0x72, (byte)0xD9, (byte)0x1C, (byte)0xAE, (byte)0xB3, (byte)0xE9, (byte)0xA0,
+                (byte)0x30, (byte)0x1B, (byte)0x8E, (byte)0x0E, (byte)0x8B, (byte)0xCB, (byte)0x74,
+                (byte)0x50, (byte)0xBB, (byte)0x3C, (byte)0x9F, (byte)0x87, (byte)0xCF, (byte)0x65,
+                (byte)0xD0, (byte)0x3D, (byte)0x4D, (byte)0x47, (byte)0x83, (byte)0xC6, (byte)0x61,
+                (byte)0xB9, (byte)0x3C, (byte)0x1D, (byte)0x3E, (byte)0x97, (byte)0x41, (byte)0xF2,
+                (byte)0x32, (byte)0xBD, (byte)0x2E, (byte)0x77, (byte)0x83, (byte)0xE0, (byte)0x61,
+                (byte)0x32, (byte)0x39, (byte)0xED, (byte)0x3E, (byte)0x37, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A GSM default alphabet message with carriage return padding",
+                msg.getMessageBody());
+
+        assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
+    }
+
+    public void testGetMessageBody8Bit() {
+        byte[] pdu = {
+                (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x44, (byte)0x11, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+                (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("8-bit message body should be empty", "", msg.getMessageBody());
+    }
+
+    public void testGetMessageBodyUcs2() {
+        byte[] pdu = {
+                (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x48, (byte)0x11, (byte)0x00,
+                (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55, (byte)0x00, (byte)0x43,
+                (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x20, (byte)0x00,
+                (byte)0x6D, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73,
+                (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65, (byte)0x00,
+                (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6E,
+                (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x69, (byte)0x00,
+                (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x67,
+                (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x20, (byte)0x04,
+                (byte)0x34, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68,
+                (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61, (byte)0x00,
+                (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x72,
+                (byte)0x00, (byte)0x0D, (byte)0x00, (byte)0x0D
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+    }
+
+    public void testGetMessageBodyUcs2WithLanguageInBody() {
+        byte[] pdu = {
+                (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x11, (byte)0x11, (byte)0x78,
+                (byte)0x3C, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55,
+                (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32, (byte)0x00,
+                (byte)0x20, (byte)0x00, (byte)0x6D, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73,
+                (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00,
+                (byte)0x65, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F,
+                (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00,
+                (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E,
+                (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61, (byte)0x00,
+                (byte)0x20, (byte)0x04, (byte)0x34, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63,
+                (byte)0x00, (byte)0x68, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00,
+                (byte)0x61, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65,
+                (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x0D
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+
+        assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
+    }
+
+    public void testGetMessageIdentifier() {
+        byte[] pdu = {
+                (byte)0xC0, (byte)0x00, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+                (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+                (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+                (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+                (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+                (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+                (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+                (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier());
+    }
+
+    public void testGetMessageCode() {
+        byte[] pdu = {
+                (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+                (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+                (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+                (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+                (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+                (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+                (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+                (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected message code decoded", 682, msg.getMessageCode());
+    }
+
+    public void testGetUpdateNumber() {
+        byte[] pdu = {
+                (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+                (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+                (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+                (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+                (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+                (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+                (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+                (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber());
+    }
+}
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index c31c9cc..3b52252 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -246,6 +246,11 @@
     }
 
     @Override
+    public void startActivities(Intent[] intents) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void startIntentSender(IntentSender intent,
             Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
             throws IntentSender.SendIntentException {
diff --git a/tests/StatusBar/AndroidManifest.xml b/tests/StatusBar/AndroidManifest.xml
index c1ca618..ddb756b 100644
--- a/tests/StatusBar/AndroidManifest.xml
+++ b/tests/StatusBar/AndroidManifest.xml
@@ -21,6 +21,17 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name="NotificationBuilderTest"
+                android:label="_Notify Builder"
+                android:theme="@android:style/Theme.Holo"
+                android:hardwareAccelerated="true"
+                >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
         <activity android:name="ToastTest" android:label="_Toasts">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -35,6 +46,8 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name="ConfirmationActivity" android:theme="@android:style/Theme.Dialog">
+        </activity>
         <activity android:name="TestAlertActivity" android:theme="@android:style/Theme.Dialog">
         </activity>
     </application>
diff --git a/tests/StatusBar/res/drawable-mdpi/emo_im_kissing.png b/tests/StatusBar/res/drawable-mdpi/emo_im_kissing.png
new file mode 100644
index 0000000..56378f6
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/emo_im_kissing.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/pineapple.png b/tests/StatusBar/res/drawable-mdpi/pineapple.png
new file mode 100644
index 0000000..7377b96
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/pineapple.png
Binary files differ
diff --git a/tests/StatusBar/res/layout/confirmation_activity.xml b/tests/StatusBar/res/layout/confirmation_activity.xml
new file mode 100644
index 0000000..50d1a49
--- /dev/null
+++ b/tests/StatusBar/res/layout/confirmation_activity.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:gravity="center_horizontal"
+    android:orientation="vertical"
+    >
+
+    <TextView android:id="@+id/text"
+        style="?android:attr/textAppearanceLarge"
+        android:padding="5dip"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:gravity="center"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <Button
+        android:id="@+id/ok"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="3"
+        android:onClick="dismiss"
+        android:text="Ok" />
+
+</LinearLayout>
diff --git a/tests/StatusBar/res/layout/notification_builder_test.xml b/tests/StatusBar/res/layout/notification_builder_test.xml
new file mode 100644
index 0000000..58c4fbb
--- /dev/null
+++ b/tests/StatusBar/res/layout/notification_builder_test.xml
@@ -0,0 +1,819 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        android:paddingLeft="40dp"
+        android:paddingTop="12dp"
+        android:paddingRight="24dp"
+        android:paddingBottom="12dp"
+        >
+
+    <LinearLayout
+            android:layout_width="220sp"
+            android:layout_height="match_parent"
+            android:layout_marginRight="24dp"
+            android:orientation="vertical"
+            >
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+            <Button
+                    style="@style/IdButton.Minus"
+                    android:id="@+id/clear_1"
+                    />
+            <TextView
+                    style="@style/IdTitle"
+                    android:text="1"
+                    />
+            <Button
+                    style="@style/IdButton.Plus"
+                    android:id="@+id/notify_1"
+                    />
+        </LinearLayout>
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+            <Button
+                    style="@style/IdButton.Minus"
+                    android:id="@+id/clear_2"
+                    />
+            <TextView
+                    style="@style/IdTitle"
+                    android:text="2"
+                    />
+            <Button
+                    style="@style/IdButton.Plus"
+                    android:id="@+id/notify_2"
+                    />
+        </LinearLayout>
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+            <Button
+                    style="@style/IdButton.Minus"
+                    android:id="@+id/clear_3"
+                    />
+            <TextView
+                    style="@style/IdTitle"
+                    android:text="3"
+                    />
+            <Button
+                    style="@style/IdButton.Plus"
+                    android:id="@+id/notify_3"
+                    />
+        </LinearLayout>
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+            <Button
+                    style="@style/IdButton.Minus"
+                    android:id="@+id/clear_4"
+                    />
+            <TextView
+                    style="@style/IdTitle"
+                    android:text="4"
+                    />
+            <Button
+                    style="@style/IdButton.Plus"
+                    android:id="@+id/notify_4"
+                    />
+        </LinearLayout>
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+            <Button
+                    style="@style/IdButton.Minus"
+                    android:id="@+id/clear_5"
+                    />
+            <TextView
+                    style="@style/IdTitle"
+                    android:text="5"
+                    />
+            <Button
+                    style="@style/IdButton.Plus"
+                    android:id="@+id/notify_5"
+                    />
+        </LinearLayout>
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+            <Button
+                    style="@style/IdButton.Minus"
+                    android:id="@+id/clear_6"
+                    />
+            <TextView
+                    style="@style/IdTitle"
+                    android:text="6"
+                    />
+            <Button
+                    style="@style/IdButton.Plus"
+                    android:id="@+id/notify_6"
+                    />
+        </LinearLayout>
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+            <Button
+                    style="@style/IdButton.Minus"
+                    android:id="@+id/clear_7"
+                    />
+            <TextView
+                    style="@style/IdTitle"
+                    android:text="7"
+                    />
+            <Button
+                    style="@style/IdButton.Plus"
+                    android:id="@+id/notify_7"
+                    />
+        </LinearLayout>
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+            <Button
+                    style="@style/IdButton.Minus"
+                    android:id="@+id/clear_8"
+                    />
+            <TextView
+                    style="@style/IdTitle"
+                    android:text="8"
+                    />
+            <Button
+                    style="@style/IdButton.Plus"
+                    android:id="@+id/notify_8"
+                    />
+        </LinearLayout>
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+            <Button
+                    style="@style/IdButton.Minus"
+                    android:id="@+id/clear_9"
+                    />
+            <TextView
+                    style="@style/IdTitle"
+                    android:text="9"
+                    />
+            <Button
+                    style="@style/IdButton.Plus"
+                    android:id="@+id/notify_9"
+                    />
+        </LinearLayout>
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                >
+            <Button
+                    style="@style/IdButton.Minus"
+                    android:id="@+id/clear_10"
+                    />
+            <TextView
+                    style="@style/IdTitle"
+                    android:text="10"
+                    />
+            <Button
+                    style="@style/IdButton.Plus"
+                    android:id="@+id/notify_10"
+                    />
+        </LinearLayout>
+
+        <Button
+                android:id="@+id/clear_all"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="12dp"
+                android:layout_marginBottom="12dp"
+                android:text="Clear All"
+                />
+        <Button
+                android:id="@+id/ten"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Ten notifications"
+                />
+                
+    </LinearLayout>
+
+    <ScrollView
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            >
+        <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                >
+
+            <!-- setWhen -->
+            <RadioGroup
+                    android:id="@+id/group_when"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setWhen"
+                        />
+                <RadioButton
+                        android:id="@+id/when_midnight"
+                        style="@style/FieldContents.Disabled"
+                        android:text="midnight"
+                        />
+                <RadioButton
+                        android:id="@+id/when_now"
+                        style="@style/FieldContents"
+                        android:text="now"
+                        />
+                <RadioButton
+                        android:id="@+id/when_now_plus_1h"
+                        style="@style/FieldContents.Disabled"
+                        android:text="now + 1h"
+                        />
+                <RadioButton
+                        android:id="@+id/when_tomorrow"
+                        style="@style/FieldContents.Disabled"
+                        android:text="tomorrow"
+                        />
+            </RadioGroup>
+
+            <!-- icon -->
+            <RadioGroup
+                    android:id="@+id/group_icon"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setSmallIcon"
+                        />
+                <RadioButton
+                        android:id="@+id/icon_im"
+                        style="@style/FieldContents"
+                        android:text="IM"
+                        />
+                <RadioButton
+                        android:id="@+id/icon_alert"
+                        style="@style/FieldContents"
+                        android:text="alert"
+                        />
+                <RadioButton
+                        android:id="@+id/icon_surprise"
+                        style="@style/FieldContents"
+                        android:text="surprise"
+                        />
+                <RadioButton
+                        android:id="@+id/icon_level0"
+                        style="@style/FieldContents.Disabled"
+                        android:text="level 0"
+                        />
+                <RadioButton
+                        android:id="@+id/icon_level50"
+                        style="@style/FieldContents.Disabled"
+                        android:text="level 50"
+                        />
+                <RadioButton
+                        android:id="@+id/icon_level100"
+                        style="@style/FieldContents.Disabled"
+                        android:text="level 100"
+                        />
+                <!-- todo setSmallIcon(int icon, int level) -->
+            </RadioGroup>
+            
+            <!-- setContentTitle -->
+            <RadioGroup
+                    android:id="@+id/group_title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setContentTitle"
+                        />
+                <RadioButton
+                        android:id="@+id/title_short"
+                        style="@style/FieldContents"
+                        android:text="none"
+                        android:tag=""
+                        />
+                <RadioButton
+                        android:id="@+id/title_short"
+                        style="@style/FieldContents"
+                        android:text="cwshort"
+                        android:tag="Title"
+                        />
+                <RadioButton
+                        android:id="@+id/title_medium"
+                        style="@style/FieldContents"
+                        android:text="medium"
+                        android:tag="Notification Test"
+                        />
+                <RadioButton
+                        android:id="@+id/title_long"
+                        style="@style/FieldContents"
+                        android:text="long"
+                        android:tag="This is one heckuva long title for a notification"
+                        />
+            </RadioGroup>
+            
+            <!-- setContentText -->
+            <RadioGroup
+                    android:id="@+id/group_text"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setContentText"
+                        />
+                <RadioButton
+                        android:id="@+id/text_none"
+                        style="@style/FieldContents"
+                        android:text="none"
+                        android:tag=""
+                        />
+                <RadioButton
+                        android:id="@+id/text_short"
+                        style="@style/FieldContents"
+                        android:tag="short"
+                        android:text="text"
+                        />
+                <RadioButton
+                        android:id="@+id/text_medium"
+                        style="@style/FieldContents"
+                        android:text="medium"
+                        android:tag="Something happened"
+                        />
+                <RadioButton
+                        android:id="@+id/text_long"
+                        style="@style/FieldContents"
+                        android:text="long"
+                        android:tag="Oh my goodness.  SOMETHING HAPPENED!!!!"
+                        />
+                <RadioButton
+                        android:id="@+id/text_haiku"
+                        style="@style/FieldContents"
+                        android:text="haiku"
+                        android:tag="sholes final approach\nlanding gear punted to flan\nrunway foam glistens"
+                        />
+            </RadioGroup>
+
+            <!-- setContentInfo -->
+            <RadioGroup
+                    android:id="@+id/group_info"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setContentInfo"
+                        />
+                <RadioButton
+                        android:id="@+id/info_none"
+                        style="@style/FieldContents"
+                        android:text="none"
+                        android:tag=""
+                        />
+                <RadioButton
+                        android:id="@+id/info_number"
+                        style="@style/FieldContents"
+                        android:text="snoozed"
+                        android:tag="snoozed"
+                        />
+                <RadioButton
+                        android:id="@+id/info_long"
+                        style="@style/FieldContents"
+                        android:text="longer"
+                        android:tag="this content info is way too long"
+                        />
+            </RadioGroup>
+
+            <!-- setNumber -->
+            <RadioGroup
+                    android:id="@+id/group_number"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setNumber"
+                        />
+                <RadioButton
+                        android:id="@+id/number_0"
+                        style="@style/FieldContents"
+                        android:text="0"
+                        android:tag="0"
+                        />
+                <RadioButton
+                        android:id="@+id/number_1"
+                        style="@style/FieldContents"
+                        android:text="1"
+                        android:tag="1"
+                        />
+                <RadioButton
+                        android:id="@+id/number_42"
+                        style="@style/FieldContents"
+                        android:text="42"
+                        android:tag="42"
+                        />
+                <RadioButton
+                        android:id="@+id/number_334"
+                        style="@style/FieldContents"
+                        android:text="334"
+                        android:tag="334"
+                        />
+                <RadioButton
+                        android:id="@+id/number_999"
+                        style="@style/FieldContents"
+                        android:text="999"
+                        android:tag="999"
+                        />
+                <RadioButton
+                        android:id="@+id/number_9876"
+                        style="@style/FieldContents"
+                        android:text="9,876"
+                        android:tag="9876"
+                        />
+                <RadioButton
+                        android:id="@+id/number_12345"
+                        style="@style/FieldContents"
+                        android:text="12,345"
+                        android:tag="12345"
+                        />
+            </RadioGroup>
+            
+            <!-- setContentIntent -->
+            <RadioGroup
+                    android:id="@+id/group_intent"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setContentIntent"
+                        />
+                <RadioButton
+                        android:id="@+id/intent_none"
+                        style="@style/FieldContents"
+                        android:text="none"
+                        />
+                <RadioButton
+                        android:id="@+id/intent_alert"
+                        style="@style/FieldContents"
+                        android:text="alert"
+                        />
+            </RadioGroup>
+            
+            <!-- setDeleteIntent -->
+            <RadioGroup
+                    android:id="@+id/group_delete"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setDeleteIntent"
+                        />
+                <RadioButton
+                        android:id="@+id/delete_none"
+                        style="@style/FieldContents"
+                        android:text="none"
+                        />
+                <RadioButton
+                        android:id="@+id/delete_alert"
+                        style="@style/FieldContents"
+                        android:text="alert"
+                        />
+            </RadioGroup>
+            
+
+            <!-- setFullScreenIntent -->
+            <RadioGroup
+                    android:id="@+id/group_full_screen"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    android:visibility="gone"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setFullScreenIntent"
+                        />
+                <RadioButton
+                        android:id="@+id/full_screen_none"
+                        style="@style/FieldContents.Disabled"
+                        android:text="none"
+                        />
+                <RadioButton
+                        android:id="@+id/full_screen_activity"
+                        style="@style/FieldContents.Disabled"
+                        android:text="full screen"
+                        />
+            </RadioGroup>
+            
+
+            <!-- setTicker -->
+            <RadioGroup
+                    android:id="@+id/group_ticker"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setTicker"
+                        />
+                <RadioButton
+                        android:id="@+id/ticker_none"
+                        style="@style/FieldContents"
+                        android:text="none"
+                        android:tag=""
+                        />
+                <RadioButton
+                        android:id="@+id/ticker_short"
+                        style="@style/FieldContents"
+                        android:text="short"
+                        android:tag="tick"
+                        />
+                <RadioButton
+                        android:id="@+id/ticker_wrap"
+                        style="@style/FieldContents"
+                        android:text="wrap"
+                        android:tag="tick tick tick tock tock tock something fun has happened but i don't know what it is just yet"
+                        />
+                <RadioButton
+                        android:id="@+id/ticker_haiku"
+                        style="@style/FieldContents"
+                        android:text="haiku"
+                        android:tag="sholes final approach\nlanding gear punted to flan\nrunway foam glistens"
+                        />
+                <RadioButton
+                        android:id="@+id/ticker_custom"
+                        style="@style/FieldContents.Disabled"
+                        android:text="custom view"
+                        />
+            </RadioGroup>
+            
+
+            <!-- setLargeIcon -->
+            <RadioGroup
+                    android:id="@+id/group_large_icon"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setLargeIcon"
+                        />
+                <RadioButton
+                        android:id="@+id/large_icon_none"
+                        style="@style/FieldContents"
+                        android:text="none"
+                        />
+                <RadioButton
+                        android:id="@+id/large_icon_pineapple"
+                        style="@style/FieldContents"
+                        android:text="pineapple"
+                        />
+            </RadioGroup>
+            
+
+            <!-- setSound -->
+            <RadioGroup
+                    android:id="@+id/group_sound"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    android:visibility="gone"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setSound"
+                        />
+                <RadioButton
+                        android:id="@+id/sound_none"
+                        style="@style/FieldContents.Disabled"
+                        android:text="none"
+                        />
+            </RadioGroup>
+            
+
+            <!-- setVibrate -->
+            <RadioGroup
+                    android:id="@+id/group_vibrate"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setVibrate"
+                        />
+                <RadioButton
+                        android:id="@+id/vibrate_none"
+                        style="@style/FieldContents"
+                        android:text="none"
+                        />
+                <RadioButton
+                        android:id="@+id/vibrate_short"
+                        style="@style/FieldContents"
+                        android:text="short"
+                        />
+                <RadioButton
+                        android:id="@+id/vibrate_medium"
+                        style="@style/FieldContents"
+                        android:text="long"
+                        />
+                <RadioButton
+                        android:id="@+id/vibrate_long"
+                        style="@style/FieldContents"
+                        android:text="long"
+                        />
+                <RadioButton
+                        android:id="@+id/vibrate_pattern"
+                        style="@style/FieldContents"
+                        android:text="longer"
+                        />
+            </RadioGroup>
+            
+
+            <!-- setLights -->
+            <RadioGroup
+                    android:id="@+id/group_lights_color"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setLights (color)"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_red"
+                        style="@style/FieldContents"
+                        android:text="red"
+                        android:tag="0xff0000"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_green"
+                        style="@style/FieldContents"
+                        android:text="green"
+                        android:tag="0x00ff00"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_blue"
+                        style="@style/FieldContents"
+                        android:text="blue"
+                        android:tag="0x0000ff"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_cyan"
+                        style="@style/FieldContents"
+                        android:text="cyan"
+                        android:tag="0x00ffff"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_magenta"
+                        style="@style/FieldContents"
+                        android:text="magenta"
+                        android:tag="0xff00ff"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_yellow"
+                        style="@style/FieldContents"
+                        android:text="yellow"
+                        android:tag="0xffff00"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_white"
+                        style="@style/FieldContents"
+                        android:text="white"
+                        android:tag="0xffffff"
+                        />
+            </RadioGroup>
+
+            <!-- setLights -->
+            <RadioGroup
+                    android:id="@+id/group_lights_blink"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="setLights (blink)"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_off"
+                        style="@style/FieldContents"
+                        android:text="off"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_slow"
+                        style="@style/FieldContents"
+                        android:text="slow"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_fast"
+                        style="@style/FieldContents"
+                        android:text="fast"
+                        />
+                <RadioButton
+                        android:id="@+id/lights_on"
+                        style="@style/FieldContents"
+                        android:text="on"
+                        />
+            </RadioGroup>
+            
+            <!-- flags -->
+            <LinearLayout
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    android:layout_marginTop="12dp"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="flags"
+                        />
+                <CheckBox
+                        android:id="@+id/flag_ongoing"
+                        style="@style/FieldContents"
+                        android:text="setOngoing"
+                        />
+                <CheckBox
+                        android:id="@+id/flag_once"
+                        style="@style/FieldContents"
+                        android:text="setOnlyAlertOnce"
+                        />
+                <CheckBox
+                        android:id="@+id/flag_auto_cancel"
+                        style="@style/FieldContents"
+                        android:text="setAutoCancel"
+                        />
+            </LinearLayout>
+            
+            <!-- defaults -->
+            <LinearLayout
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    >
+                <TextView
+                        style="@style/FieldTitle"
+                        android:text="defaults"
+                        />
+                <CheckBox
+                        android:id="@+id/default_sound"
+                        style="@style/FieldContents"
+                        android:text="sound"
+                        />
+                <CheckBox
+                        android:id="@+id/default_vibrate"
+                        style="@style/FieldContents"
+                        android:text="vibrate"
+                        />
+                <CheckBox
+                        android:id="@+id/default_lights"
+                        style="@style/FieldContents"
+                        android:text="lights"
+                        />
+            </LinearLayout>
+            
+
+
+
+        </LinearLayout>
+    </ScrollView>
+
+
+</LinearLayout>
diff --git a/tests/StatusBar/res/values/styles.xml b/tests/StatusBar/res/values/styles.xml
new file mode 100644
index 0000000..e051efd
--- /dev/null
+++ b/tests/StatusBar/res/values/styles.xml
@@ -0,0 +1,63 @@
+<?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>
+
+    <style name="IdTitle">
+        <item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
+        <item name="android:layout_width">30sp</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">center</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="IdButton">
+        <item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
+        <item name="android:layout_width">0dp</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_weight">1</item>
+        <item name="android:layout_marginRight">8dp</item>
+        <item name="android:layout_marginLeft">8dp</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="IdButton.Minus">
+        <item name="android:text">-</item>
+    </style>
+
+    <style name="IdButton.Plus">
+        <item name="android:text">+</item>
+    </style>
+
+    <style name="FieldTitle">
+        <item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
+        <item name="android:layout_width">208sp</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+
+    <style name="FieldContents">
+        <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginRight">20dp</item>
+    </style>
+
+    <style name="FieldContents.Disabled">
+        <item name="android:clickable">false</item>
+        <item name="android:visibility">gone</item>
+    </style>
+
+</resources>
+
diff --git a/tests/StatusBar/src/com/android/statusbartest/ConfirmationActivity.java b/tests/StatusBar/src/com/android/statusbartest/ConfirmationActivity.java
new file mode 100644
index 0000000..5ce8f3f
--- /dev/null
+++ b/tests/StatusBar/src/com/android/statusbartest/ConfirmationActivity.java
@@ -0,0 +1,32 @@
+package com.android.statusbartest;
+
+import android.app.Activity;
+import android.view.View;
+import android.widget.TextView;
+
+public class ConfirmationActivity extends Activity {
+    public static final String EXTRA_TITLE = "title";
+    public static final String EXTRA_TEXT = "text";
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        setContentView(R.layout.confirmation_activity);
+        setTitle(getTextExtra(EXTRA_TITLE, "Title"));
+        ((TextView)findViewById(R.id.text)).setText(getTextExtra(EXTRA_TEXT, "text"));
+        findViewById(R.id.ok).setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    finish();
+                }
+            });
+    }
+
+    private String getTextExtra(String extra, String def) {
+        final String text = getIntent().getStringExtra(extra);
+        if (text == null) {
+            return def;
+        } else {
+            return text;
+        }
+    }
+}
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
new file mode 100644
index 0000000..e9a3513c
--- /dev/null
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.statusbartest;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Vibrator;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.RemoteViews;
+import android.os.PowerManager;
+
+public class NotificationBuilderTest extends Activity
+{
+    private final static String TAG = "NotificationTestList";
+
+    NotificationManager mNM;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+        setContentView(R.layout.notification_builder_test);
+        if (icicle == null) {
+            setDefaults();
+        }
+        for (int id: new int[] {
+                    R.id.clear_1,
+                    R.id.clear_2,
+                    R.id.clear_3,
+                    R.id.clear_4,
+                    R.id.clear_5,
+                    R.id.clear_6,
+                    R.id.clear_7,
+                    R.id.clear_8,
+                    R.id.clear_9,
+                    R.id.clear_10,
+                    R.id.notify_1,
+                    R.id.notify_2,
+                    R.id.notify_3,
+                    R.id.notify_4,
+                    R.id.notify_5,
+                    R.id.notify_6,
+                    R.id.notify_7,
+                    R.id.notify_8,
+                    R.id.notify_9,
+                    R.id.notify_10,
+                    R.id.ten,
+                    R.id.clear_all,
+                }) {
+            findViewById(id).setOnClickListener(mClickListener);
+        }
+    }
+
+    private void setDefaults() {
+        setChecked(R.id.when_now);
+        setChecked(R.id.icon_surprise);
+        setChecked(R.id.title_medium);
+        setChecked(R.id.text_medium);
+        setChecked(R.id.info_none);
+        setChecked(R.id.number_0);
+        setChecked(R.id.intent_alert);
+        setChecked(R.id.delete_none);
+        setChecked(R.id.full_screen_none);
+        setChecked(R.id.ticker_none);
+        setChecked(R.id.large_icon_none);
+        setChecked(R.id.sound_none);
+        setChecked(R.id.vibrate_none);
+        setChecked(R.id.lights_red);
+        setChecked(R.id.lights_off);
+    }
+
+    private View.OnClickListener mClickListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            switch (v.getId()) {
+                case R.id.clear_1:
+                    mNM.cancel(1);
+                    break;
+                case R.id.clear_2:
+                    mNM.cancel(2);
+                    break;
+                case R.id.clear_3:
+                    mNM.cancel(3);
+                    break;
+                case R.id.clear_4:
+                    mNM.cancel(4);
+                    break;
+                case R.id.clear_5:
+                    mNM.cancel(5);
+                    break;
+                case R.id.clear_6:
+                    mNM.cancel(6);
+                    break;
+                case R.id.clear_7:
+                    mNM.cancel(7);
+                    break;
+                case R.id.clear_8:
+                    mNM.cancel(8);
+                    break;
+                case R.id.clear_9:
+                    mNM.cancel(9);
+                    break;
+                case R.id.clear_10:
+                    mNM.cancel(10);
+                    break;
+                case R.id.notify_1:
+                    sendNotification(1);
+                    break;
+                case R.id.notify_2:
+                    sendNotification(2);
+                    break;
+                case R.id.notify_3:
+                    sendNotification(3);
+                    break;
+                case R.id.notify_4:
+                    sendNotification(4);
+                    break;
+                case R.id.notify_5:
+                    sendNotification(5);
+                    break;
+                case R.id.notify_6:
+                    sendNotification(6);
+                    break;
+                case R.id.notify_7:
+                    sendNotification(7);
+                    break;
+                case R.id.notify_8:
+                    sendNotification(8);
+                    break;
+                case R.id.notify_9:
+                    sendNotification(9);
+                    break;
+                case R.id.notify_10:
+                    sendNotification(10);
+                    break;
+                case R.id.ten: {
+                    for (int id=1; id<=10; id++) {
+                        sendNotification(id);
+                    }
+                    break;
+                }
+                case R.id.clear_all: {
+                    for (int id=1; id<=10; id++) {
+                        mNM.cancel(id);
+                    }
+                    break;
+                }
+            }
+        }
+    };
+
+    private void sendNotification(int id) {
+        final Notification n = buildNotification(id);
+        mNM.notify(id, n);
+    }
+
+    private Notification buildNotification(int id) {
+        Notification.Builder b = new Notification.Builder(this);
+
+        // when
+        switch (getRadioChecked(R.id.group_when)) {
+            case R.id.when_midnight:
+                break;
+            case R.id.when_now:
+                b.setWhen(System.currentTimeMillis());
+                break;
+            case R.id.when_now_plus_1h:
+                break;
+            case R.id.when_tomorrow:
+                break;
+        }
+
+        // icon
+        switch (getRadioChecked(R.id.group_icon)) {
+            case R.id.icon_im:
+                b.setSmallIcon(R.drawable.icon1);
+                break;
+            case R.id.icon_alert:
+                b.setSmallIcon(R.drawable.icon2);
+                break;
+            case R.id.icon_surprise:
+                b.setSmallIcon(R.drawable.emo_im_kissing);
+                break;
+        }
+
+        // title
+        final String title = getRadioTag(R.id.group_title);
+        if (!TextUtils.isEmpty(title)) {
+            b.setContentTitle(title);
+        }
+
+        // text
+        final String text = getRadioTag(R.id.group_text);
+        if (!TextUtils.isEmpty(text)) {
+            b.setContentText(text);
+        }
+
+        // info
+        final String info = getRadioTag(R.id.group_info);
+        if (!TextUtils.isEmpty(info)) {
+            b.setContentInfo(info);
+        }
+
+        // number
+        b.setNumber(getRadioInt(R.id.group_number, 0));
+
+        // contentIntent
+        switch (getRadioChecked(R.id.group_intent)) {
+            case R.id.intent_none:
+                break;
+            case R.id.intent_alert:
+                b.setContentIntent(makeContentIntent(id));
+                break;
+        }
+
+        // deleteIntent
+        switch (getRadioChecked(R.id.group_delete)) {
+            case R.id.delete_none:
+                break;
+            case R.id.delete_alert:
+                b.setDeleteIntent(makeDeleteIntent(id));
+                break;
+        }
+
+        // fullScreenIntent TODO
+
+        // ticker
+        switch (getRadioChecked(R.id.group_ticker)) {
+            case R.id.ticker_none:
+                break;
+            case R.id.ticker_short:
+            case R.id.ticker_wrap:
+            case R.id.ticker_haiku:
+                b.setTicker(getRadioTag(R.id.group_ticker));
+                break;
+            case R.id.ticker_custom:
+                // TODO
+                break;
+        }
+
+        // largeIcon
+        switch (getRadioChecked(R.id.group_large_icon)) {
+            case R.id.large_icon_none:
+                break;
+            case R.id.large_icon_pineapple:
+                b.setLargeIcon(loadBitmap(R.drawable.pineapple));
+                break;
+        }
+
+        // sound TODO
+
+        // vibrate
+        switch (getRadioChecked(R.id.group_vibrate)) {
+            case R.id.vibrate_none:
+                break;
+            case R.id.vibrate_short:
+                b.setVibrate(new long[] { 0, 200 });
+                break;
+            case R.id.vibrate_medium:
+                b.setVibrate(new long[] { 0, 500 });
+                break;
+            case R.id.vibrate_long:
+                b.setVibrate(new long[] { 0, 1000 });
+                break;
+            case R.id.vibrate_pattern:
+                b.setVibrate(new long[] { 0, 250, 250, 250, 250, 250, 250, 250 });
+                break;
+        }
+
+        // lights
+        final int color = getRadioInt(R.id.group_lights_color, 0xff0000);
+        int onMs;
+        int offMs;
+        switch (getRadioChecked(R.id.group_lights_blink)) {
+            case R.id.lights_slow:
+                onMs = 1300;
+                offMs = 1300;
+                break;
+            case R.id.lights_fast:
+                onMs = 300;
+                offMs = 300;
+                break;
+            case R.id.lights_on:
+                onMs = 1;
+                offMs = 0;
+                break;
+            case R.id.lights_off:
+            default:
+                onMs = 0;
+                offMs = 0;
+                break;
+        }
+        if (onMs != 0 && offMs != 0) {
+            b.setLights(color, onMs, offMs);
+        }
+
+        // flags
+        b.setOngoing(getChecked(R.id.flag_ongoing));
+        b.setOnlyAlertOnce(getChecked(R.id.flag_once));
+        b.setAutoCancel(getChecked(R.id.flag_auto_cancel));
+
+        // defaults
+        int defaults = 0;
+        if (getChecked(R.id.default_sound)) {
+            defaults |= Notification.DEFAULT_SOUND;
+        }
+        if (getChecked(R.id.default_vibrate)) {
+            defaults |= Notification.DEFAULT_VIBRATE;
+        }
+        if (getChecked(R.id.default_lights)) {
+            defaults |= Notification.DEFAULT_LIGHTS;
+        }
+        b.setDefaults(defaults);
+
+        return b.getNotification();
+    }
+
+    private void setChecked(int id) {
+        final CompoundButton b = (CompoundButton)findViewById(id);
+        b.setChecked(true);
+    }
+
+    private int getRadioChecked(int id) {
+        final RadioGroup g = (RadioGroup)findViewById(id);
+        return g.getCheckedRadioButtonId();
+    }
+
+    private String getRadioTag(int id) {
+        final RadioGroup g = (RadioGroup)findViewById(id);
+        final View v = findViewById(g.getCheckedRadioButtonId());
+        return (String)v.getTag();
+    }
+
+    private int getRadioInt(int id, int def) {
+        String str = getRadioTag(id);
+        if (TextUtils.isEmpty(str)) {
+            return def;
+        } else {
+            try {
+                return Integer.parseInt(str);
+            } catch (NumberFormatException ex) {
+                return def;
+            }
+        }
+    }
+
+    private boolean getChecked(int id) {
+        final CompoundButton b = (CompoundButton)findViewById(id);
+        return b.isChecked();
+    }
+    
+    private Bitmap loadBitmap(int id) {
+        final BitmapDrawable bd = (BitmapDrawable)getResources().getDrawable(id);
+        return Bitmap.createBitmap(bd.getBitmap());
+    }
+
+    private PendingIntent makeDeleteIntent(int id) {
+        Intent intent = new Intent(this, ConfirmationActivity.class);
+        intent.setData(Uri.fromParts("content", "//status_bar_test/delete/" + id, null));
+        intent.putExtra(ConfirmationActivity.EXTRA_TITLE, "Delete intent");
+        intent.putExtra(ConfirmationActivity.EXTRA_TEXT, "id: " + id);
+        return PendingIntent.getActivity(this, 0, intent, 0);
+    }
+
+    private PendingIntent makeContentIntent(int id) {
+        Intent intent = new Intent(this, ConfirmationActivity.class);
+        intent.setData(Uri.fromParts("content", "//status_bar_test/content/" + id, null));
+        intent.putExtra(ConfirmationActivity.EXTRA_TITLE, "Content intent");
+        intent.putExtra(ConfirmationActivity.EXTRA_TEXT, "id: " + id);
+        return PendingIntent.getActivity(this, 0, intent, 0);
+    }
+}
+
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 2df97dc..0f0637f 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -135,95 +135,6 @@
             }
         },
 
-        new Test("Ticker 1 line & icon") {
-            public void run() {
-                Notification n = new Notification(R.drawable.icon1, "tick tick tick",
-                        mActivityCreateTime);
-                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
-                            "This is a notification!!!", makeIntent());
-                n.tickerIcons = new Bitmap[1];
-                n.tickerIcons[0] = loadBitmap(R.drawable.icon3);
-                mNM.notify(1, n);
-            }
-        },
-
-        new Test("Ticker 2 lines") {
-            public void run() {
-                Notification n = new Notification(R.drawable.icon1, "tick tick tick\ntock tock",
-                        mActivityCreateTime);
-                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
-                            "This is a notification!!!", makeIntent());
-                mNM.notify(1, n);
-            }
-        },
-
-        new Test("Ticker title") {
-            public void run() {
-                Notification n = new Notification(R.drawable.icon1, null,
-                        mActivityCreateTime);
-                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
-                            "This is a notification!!!", makeIntent());
-                n.tickerTitle = "This is a title";
-                mNM.notify(1, n);
-            }
-        },
-
-        new Test("Ticker subtitle") {
-            public void run() {
-                Notification n = new Notification(R.drawable.icon1, null,
-                        mActivityCreateTime);
-                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
-                            "This is a notification!!!", makeIntent());
-                n.tickerSubtitle = "and a subtitle";
-                mNM.notify(1, n);
-            }
-        },
-
-        new Test("Ticker title & subtitle") {
-            public void run() {
-                Notification n = new Notification(R.drawable.icon1, null,
-                        mActivityCreateTime);
-                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
-                            "This is a notification!!!", makeIntent());
-                n.tickerTitle = "This is a title it is really really longggggg long long long long";
-                n.tickerSubtitle = "and a subtitle it is really really longggggg long long long long long long long long long long long long long long long long";
-                mNM.notify(1, n);
-            }
-        },
-
-        new Test("Ticker text, title & subtitle") {
-            public void run() {
-                Notification n = new Notification(R.drawable.icon1, "not visible",
-                        mActivityCreateTime);
-                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
-                            "This is a notification!!!", makeIntent());
-                n.tickerTitle = "This is a title";
-                n.tickerSubtitle = "and a subtitle";
-                mNM.notify(1, n);
-            }
-        },
-
-        new Test("Ticker title, subtitle & 2 icons") {
-            public void run() {
-                Notification n = new Notification(R.drawable.icon1, null,
-                        mActivityCreateTime);
-                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
-                            "This is a notification!!!", makeIntent());
-                n.tickerTitle = "This is a title";
-                n.tickerSubtitle = "and a subtitle";
-
-                n.tickerIcons = new Bitmap[2];
-                n.tickerIcons[0] = loadBitmap(R.drawable.icon3);
-                n.tickerIcons[1] = loadBitmap(R.drawable.app_gmail);
-
-                mNM.notify(1, n);
-                /*
-                n.tickerIcons[0].recycle();
-                n.tickerIcons[1].recycle();
-                */
-            }
-        },
-
         new Test("No view") {
             public void run() {
                 Notification n = new Notification(R.drawable.icon1, "No view",
diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
index fec3671..4cacbc4 100644
--- a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
@@ -39,7 +39,7 @@
     @SmallTest
 	public void testREORDER_TASKS() {
         try {
-            mAm.moveTaskToFront(-1);
+            mAm.moveTaskToFront(0, 0);
             fail("IActivityManager.moveTaskToFront did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 3cb614f..faae89b 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -33,8 +33,8 @@
 
 /* fwd decls, so I can write this downward */
 ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
-                        const sp<AaptDir>& dir, const AaptGroupEntry& ge);
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
+                        const AaptGroupEntry& ge, const ResourceFilter* filter);
 bool processFile(Bundle* bundle, ZipFile* zip,
                         const sp<AaptGroup>& group, const sp<AaptFile>& file);
 bool okayToCompress(Bundle* bundle, const String8& pathName);
@@ -204,34 +204,45 @@
     const size_t N = assets->getGroupEntries().size();
     for (size_t i=0; i<N; i++) {
         const AaptGroupEntry& ge = assets->getGroupEntries()[i];
-        if (!filter.match(ge.toParams())) {
-            continue;
-        }
-        ssize_t res = processAssets(bundle, zip, assets, ge);
+
+        ssize_t res = processAssets(bundle, zip, assets, ge, &filter);
         if (res < 0) {
             return res;
         }
+
         count += res;
     }
 
     return count;
 }
 
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
-                      const sp<AaptDir>& dir, const AaptGroupEntry& ge)
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
+        const AaptGroupEntry& ge, const ResourceFilter* filter)
 {
     ssize_t count = 0;
 
     const size_t ND = dir->getDirs().size();
     size_t i;
     for (i=0; i<ND; i++) {
-        ssize_t res = processAssets(bundle, zip, dir->getDirs().valueAt(i), ge);
+        const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
+
+        const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0;
+
+        if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) {
+            continue;
+        }
+
+        ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL);
         if (res < 0) {
             return res;
         }
         count += res;
     }
 
+    if (filter != NULL && !filter->match(ge.toParams())) {
+        return count;
+    }
+
     const size_t NF = dir->getFiles().size();
     for (i=0; i<NF; i++) {
         sp<AaptGroup> gp = dir->getFiles().valueAt(i);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 822262e..7b74506 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -150,7 +150,7 @@
 {
     return type == "anim" || type == "drawable" || type == "layout"
         || type == "values" || type == "xml" || type == "raw"
-        || type == "color" || type == "menu";
+        || type == "color" || type == "menu" || type == "mipmap";
 }
 
 static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
@@ -284,9 +284,9 @@
 }
 
 static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
-                          const sp<ResourceTypeSet>& set)
+                          const sp<ResourceTypeSet>& set, const char* type)
 {
-    ResourceDirIterator it(set, String8("drawable"));
+    ResourceDirIterator it(set, String8(type));
     Vector<sp<AaptFile> > newNameFiles;
     Vector<String8> newNamePaths;
     bool hasErrors = false;
@@ -802,6 +802,7 @@
     sp<ResourceTypeSet> raws;
     sp<ResourceTypeSet> colors;
     sp<ResourceTypeSet> menus;
+    sp<ResourceTypeSet> mipmaps;
 
     ASSIGN_IT(drawable);
     ASSIGN_IT(layout);
@@ -810,6 +811,7 @@
     ASSIGN_IT(raw);
     ASSIGN_IT(color);
     ASSIGN_IT(menu);
+    ASSIGN_IT(mipmap);
 
     assets->setResources(resources);
     // now go through any resource overlays and collect their files
@@ -828,7 +830,8 @@
             !applyFileOverlay(bundle, assets, &xmls, "xml") ||
             !applyFileOverlay(bundle, assets, &raws, "raw") ||
             !applyFileOverlay(bundle, assets, &colors, "color") ||
-            !applyFileOverlay(bundle, assets, &menus, "menu")) {
+            !applyFileOverlay(bundle, assets, &menus, "menu") ||
+            !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
         return UNKNOWN_ERROR;
     }
 
@@ -836,7 +839,7 @@
 
     if (drawables != NULL) {
         if (bundle->getOutputAPKFile() != NULL) {
-            err = preProcessImages(bundle, assets, drawables);
+            err = preProcessImages(bundle, assets, drawables, "drawable");
         }
         if (err == NO_ERROR) {
             err = makeFileResources(bundle, assets, &table, drawables, "drawable");
@@ -848,6 +851,20 @@
         }
     }
 
+    if (mipmaps != NULL) {
+        if (bundle->getOutputAPKFile() != NULL) {
+            err = preProcessImages(bundle, assets, mipmaps, "mipmap");
+        }
+        if (err == NO_ERROR) {
+            err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
+            if (err != NO_ERROR) {
+                hasErrors = true;
+            }
+        } else {
+            hasErrors = true;
+        }
+    }
+
     if (layouts != NULL) {
         err = makeFileResources(bundle, assets, &table, layouts, "layout");
         if (err != NO_ERROR) {
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index a77042a..196b06c 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2539,7 +2539,7 @@
 }
 
 bool
-ResourceFilter::match(int axis, uint32_t value)
+ResourceFilter::match(int axis, uint32_t value) const
 {
     if (value == 0) {
         // they didn't specify anything so take everything
@@ -2555,7 +2555,7 @@
 }
 
 bool
-ResourceFilter::match(const ResTable_config& config)
+ResourceFilter::match(const ResTable_config& config) const
 {
     if (config.locale) {
         uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
@@ -2608,6 +2608,8 @@
     const size_t N = mOrderedPackages.size();
     size_t pi;
 
+    const static String16 mipmap16("mipmap");
+
     bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
 
     // Iterate through all data, collecting all values (strings,
@@ -2630,7 +2632,10 @@
                 typeStrings.add(String16("<empty>"), false);
                 continue;
             }
-            typeStrings.add(t->getName(), false);
+            const String16 typeName(t->getName());
+            typeStrings.add(typeName, false);
+
+            const bool filterable = (typeName != mipmap16);
 
             const size_t N = t->getOrderedConfigs().size();
             for (size_t ci=0; ci<N; ci++) {
@@ -2641,7 +2646,7 @@
                 const size_t N = c->getEntries().size();
                 for (size_t ei=0; ei<N; ei++) {
                     ConfigDescription config = c->getEntries().keyAt(ei);
-                    if (!filter.match(config)) {
+                    if (filterable && !filter.match(config)) {
                         continue;
                     }
                     sp<Entry> e = c->getEntries().valueAt(ei);
@@ -2721,6 +2726,8 @@
                                 "Type name %s not found",
                                 String8(typeName).string());
 
+            const bool filterable = (typeName != mipmap16);
+
             const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
             
             // First write the typeSpec chunk, containing information about
@@ -2745,7 +2752,7 @@
                     (((uint8_t*)data->editData())
                         + typeSpecStart + sizeof(ResTable_typeSpec));
                 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
-                        
+
                 for (size_t ei=0; ei<N; ei++) {
                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
                     if (cl->getPublic()) {
@@ -2753,11 +2760,11 @@
                     }
                     const size_t CN = cl->getEntries().size();
                     for (size_t ci=0; ci<CN; ci++) {
-                        if (!filter.match(cl->getEntries().keyAt(ci))) {
+                        if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
                             continue;
                         }
                         for (size_t cj=ci+1; cj<CN; cj++) {
-                            if (!filter.match(cl->getEntries().keyAt(cj))) {
+                            if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
                                 continue;
                             }
                             typeSpecFlags[ei] |= htodl(
@@ -2794,7 +2801,7 @@
                       config.screenWidth,
                       config.screenHeight));
                       
-                if (!filter.match(config)) {
+                if (filterable && !filter.match(config)) {
                     continue;
                 }
                 
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index 186c7ca..bbb8140 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -549,9 +549,9 @@
 public:
     ResourceFilter() : mData(), mContainsPseudo(false) {}
     status_t parse(const char* arg);
-    bool match(int axis, uint32_t value);
-    bool match(const ResTable_config& config);
-    inline bool containsPseudo() { return mContainsPseudo; }
+    bool match(int axis, uint32_t value) const;
+    bool match(const ResTable_config& config) const;
+    inline bool containsPseudo() const { return mContainsPseudo; }
 
 private:
     KeyedVector<int,SortedVector<uint32_t> > mData;
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
index 6fd59c4..212223c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
@@ -456,7 +456,11 @@
             // into is.read(...) This number is not related to the value passed
             // to mark(...) above.
             try {
-                bm = Bitmap_Delegate.createBitmap(is, Density.MEDIUM);
+                Density density = Density.MEDIUM;
+                if (opts != null) {
+                    density = Density.getEnum(opts.inDensity);
+                }
+                bm = Bitmap_Delegate.createBitmap(is, true, density);
             } catch (IOException e) {
                 return null;
             }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index 0920497..b4c51b2 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -75,32 +75,56 @@
 
     /**
      * Creates and returns a {@link Bitmap} initialized with the given file content.
+     *
+     * @param input the file from which to read the bitmap content
+     * @param isMutable whether the bitmap is mutable
+     * @param density the density associated with the bitmap
+     *
+     * @see Bitmap#isMutable()
+     * @see Bitmap#getDensity()
      */
-    public static Bitmap createBitmap(File input, Density density) throws IOException {
+    public static Bitmap createBitmap(File input, boolean isMutable, Density density)
+            throws IOException {
         // create a delegate with the content of the file.
         Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input));
 
-        return createBitmap(delegate, density.getValue());
+        return createBitmap(delegate, isMutable, density.getValue());
     }
 
     /**
      * Creates and returns a {@link Bitmap} initialized with the given stream content.
+     *
+     * @param input the stream from which to read the bitmap content
+     * @param isMutable whether the bitmap is mutable
+     * @param density the density associated with the bitmap
+     *
+     * @see Bitmap#isMutable()
+     * @see Bitmap#getDensity()
      */
-    public static Bitmap createBitmap(InputStream input, Density density) throws IOException {
+    public static Bitmap createBitmap(InputStream input, boolean isMutable, Density density)
+            throws IOException {
         // create a delegate with the content of the stream.
         Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input));
 
-        return createBitmap(delegate, density.getValue());
+        return createBitmap(delegate, isMutable, density.getValue());
     }
 
     /**
      * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
+     *
+     * @param image the bitmap content
+     * @param isMutable whether the bitmap is mutable
+     * @param density the density associated with the bitmap
+     *
+     * @see Bitmap#isMutable()
+     * @see Bitmap#getDensity()
      */
-    public static Bitmap createBitmap(BufferedImage image, Density density) throws IOException {
+    public static Bitmap createBitmap(BufferedImage image, boolean isMutable, Density density)
+            throws IOException {
         // create a delegate with the given image.
         Bitmap_Delegate delegate = new Bitmap_Delegate(image);
 
-        return createBitmap(delegate, density.getValue());
+        return createBitmap(delegate, isMutable, density.getValue());
     }
 
     /**
@@ -153,7 +177,7 @@
         // create a delegate with the content of the stream.
         Bitmap_Delegate delegate = new Bitmap_Delegate(image);
 
-        return createBitmap(delegate, Bitmap.getDefaultDensity());
+        return createBitmap(delegate, mutable, Bitmap.getDefaultDensity());
     }
 
     /*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) {
@@ -166,8 +190,7 @@
     }
 
     /*package*/ static void nativeRecycle(int nativeBitmap) {
-        // FIXME implement native delegate
-        throw new UnsupportedOperationException("Native delegate needed for Bitmap");
+        sManager.removeDelegate(nativeBitmap);
     }
 
     /*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality,
@@ -336,11 +359,11 @@
         mImage = image;
     }
 
-    private static Bitmap createBitmap(Bitmap_Delegate delegate, int density) {
+    private static Bitmap createBitmap(Bitmap_Delegate delegate, boolean isMutable, int density) {
         // get its native_int
         int nativeInt = sManager.addDelegate(delegate);
 
         // and create/return a new Bitmap with it
-        return new Bitmap(nativeInt, true /*isMutable*/, null /*ninePatchChunk*/, density);
+        return new Bitmap(nativeInt, isMutable, null /*ninePatchChunk*/, density);
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index cea07af..08f3c7a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -960,7 +960,7 @@
      * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
      * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
      */
-    private Graphics2D getCustomGraphics(Paint_Delegate paint) {
+    /*package*/ Graphics2D getCustomGraphics(Paint_Delegate paint) {
         // make new one
         Graphics2D g = getGraphics2d();
         g = (Graphics2D)g.create();
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
new file mode 100644
index 0000000..3d26e47
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.ninepatch.NinePatchChunk;
+
+import android.graphics.drawable.NinePatchDrawable;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.ref.SoftReference;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Delegate implementing the native methods of android.graphics.NinePatch
+ *
+ * Through the layoutlib_create tool, the original native methods of NinePatch have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ *
+ */
+public class NinePatch_Delegate {
+
+    /**
+     * Cache map for {@link NinePatchChunk}.
+     * When the chunks are created they are serialized into a byte[], and both are put
+     * in the cache, using a {@link SoftReference} for the chunk. The default Java classes
+     * for {@link NinePatch} and {@link NinePatchDrawable} only reference to the byte[] data, and
+     * provide this for drawing.
+     * Using the cache map allows us to not have to deserialize the byte[] back into a
+     * {@link NinePatchChunk} every time a rendering is done.
+     */
+    private final static Map<byte[], SoftReference<NinePatchChunk>> sChunkCache =
+        new HashMap<byte[], SoftReference<NinePatchChunk>>();
+
+    // ---- Public Helper methods ----
+
+    /**
+     * Serializes the given chunk.
+     *
+     * @return the serialized data for the chunk.
+     */
+    public static byte[] serialize(NinePatchChunk chunk) {
+        // serialize the chunk to get a byte[]
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = null;
+        try {
+            oos = new ObjectOutputStream(baos);
+            oos.writeObject(chunk);
+        } catch (IOException e) {
+            //FIXME log this.
+            return null;
+        } finally {
+            if (oos != null) {
+                try {
+                    oos.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+
+        // get the array and add it to the cache
+        byte[] array = baos.toByteArray();
+        sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
+        return array;
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static boolean isNinePatchChunk(byte[] chunk) {
+        NinePatchChunk chunkObject = getChunk(chunk);
+        if (chunkObject != null) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /*package*/ static void validateNinePatchChunk(int bitmap, byte[] chunk) {
+        // the default JNI implementation only checks that the byte[] has the same
+        // size as the C struct it represent. Since we cannot do the same check (serialization
+        // will return different size depending on content), we do nothing.
+    }
+
+    /*package*/ static void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance,
+            byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
+        draw(canvas_instance,
+                (int) loc.left, (int) loc.top, (int) loc.width(), (int) loc.height(),
+                bitmap_instance, c, paint_instance_or_null,
+                destDensity, srcDensity);
+    }
+
+    /*package*/ static void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance,
+            byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
+        draw(canvas_instance,
+                loc.left, loc.top, loc.width(), loc.height(),
+                bitmap_instance, c, paint_instance_or_null,
+                destDensity, srcDensity);
+    }
+
+   private static void draw(int canvas_instance,
+           int left, int top, int right, int bottom,
+           int bitmap_instance, byte[] c, int paint_instance_or_null,
+           int destDensity, int srcDensity) {
+       // get the delegate from the native int.
+       Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance);
+       if (bitmap_delegate == null) {
+           assert false;
+           return;
+       }
+
+       if (c == null) {
+           // not a 9-patch?
+           BufferedImage image = bitmap_delegate.getImage();
+           Canvas_Delegate.native_drawBitmap(canvas_instance, bitmap_instance,
+                   new Rect(0, 0, image.getWidth(), image.getHeight()),
+                   new Rect(left, top, right, bottom),
+                   paint_instance_or_null, destDensity, srcDensity);
+           return;
+       }
+
+       NinePatchChunk chunkObject = getChunk(c);
+       assert chunkObject != null;
+       if (chunkObject == null) {
+           return;
+       }
+
+       Canvas_Delegate canvas_delegate = Canvas_Delegate.getDelegate(canvas_instance);
+       if (canvas_delegate == null) {
+           assert false;
+           return;
+       }
+
+       // this one can be null
+       Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null);
+
+       Graphics2D graphics;
+       if (paint_delegate != null) {
+           graphics = canvas_delegate.getCustomGraphics(paint_delegate);
+       } else {
+           graphics = canvas_delegate.getGraphics2d();
+       }
+
+       try {
+           chunkObject.draw(bitmap_delegate.getImage(), graphics,
+                   left, top, right - left, bottom - top);
+       } finally {
+           if (paint_delegate != null) {
+               graphics.dispose();
+           }
+       }
+
+    }
+
+    /*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) {
+        return 0;
+    }
+
+    // ---- Private Helper methods ----
+
+    /**
+     * Returns a {@link NinePatchChunk} object for the given serialized representation.
+     *
+     * If the chunk is present in the cache then the object from the cache is returned, otherwise
+     * the array is deserialized into a {@link NinePatchChunk} object.
+     *
+     * @param array the serialized representation of the chunk.
+     * @return the NinePatchChunk or null if deserialization failed.
+     */
+    private static NinePatchChunk getChunk(byte[] array) {
+        SoftReference<NinePatchChunk> chunkRef = sChunkCache.get(array);
+        NinePatchChunk chunk = chunkRef.get();
+        if (chunk == null) {
+            ByteArrayInputStream bais = new ByteArrayInputStream(array);
+            ObjectInputStream ois = null;
+            try {
+                ois = new ObjectInputStream(bais);
+                chunk = (NinePatchChunk) ois.readObject();
+
+                // put back the chunk in the cache
+                if (chunk != null) {
+                    sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
+                }
+            } catch (IOException e) {
+                // FIXME: log this
+                return null;
+            } catch (ClassNotFoundException e) {
+                // FIXME: log this
+                return null;
+            } finally {
+                if (ois != null) {
+                    try {
+                        ois.close();
+                    } catch (IOException e) {
+                    }
+                }
+            }
+        }
+
+        return chunk;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index e691fdf..35ba73d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -26,7 +26,7 @@
 import com.android.layoutlib.bridge.android.BridgeAssetManager;
 import com.android.layoutlib.bridge.impl.FontLoader;
 import com.android.layoutlib.bridge.impl.LayoutSceneImpl;
-import com.android.ninepatch.NinePatch;
+import com.android.ninepatch.NinePatchChunk;
 import com.android.tools.layoutlib.create.MethodAdapter;
 import com.android.tools.layoutlib.create.OverrideMethod;
 
@@ -73,13 +73,13 @@
 
     private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache =
         new HashMap<Object, Map<String, SoftReference<Bitmap>>>();
-    private final static Map<Object, Map<String, SoftReference<NinePatch>>> sProject9PatchCache =
-        new HashMap<Object, Map<String, SoftReference<NinePatch>>>();
+    private final static Map<Object, Map<String, SoftReference<NinePatchChunk>>> sProject9PatchCache =
+        new HashMap<Object, Map<String, SoftReference<NinePatchChunk>>>();
 
     private final static Map<String, SoftReference<Bitmap>> sFrameworkBitmapCache =
         new HashMap<String, SoftReference<Bitmap>>();
-    private final static Map<String, SoftReference<NinePatch>> sFramework9PatchCache =
-        new HashMap<String, SoftReference<NinePatch>>();
+    private final static Map<String, SoftReference<NinePatchChunk>> sFramework9PatchCache =
+        new HashMap<String, SoftReference<NinePatchChunk>>();
 
     private static Map<String, Map<String, Integer>> sEnumValueMap;
 
@@ -252,23 +252,23 @@
     }
 
     /**
-     * Sets a 9 patch in a project cache or in the framework cache.
+     * Sets a 9 patch chunk in a project cache or in the framework cache.
      * @param value the path of the 9 patch
      * @param ninePatch the 9 patch object
      * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
      */
-    public static void setCached9Patch(String value, NinePatch ninePatch, Object projectKey) {
+    public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) {
         if (projectKey != null) {
-            Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey);
+            Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
 
             if (map == null) {
-                map = new HashMap<String, SoftReference<NinePatch>>();
+                map = new HashMap<String, SoftReference<NinePatchChunk>>();
                 sProject9PatchCache.put(projectKey, map);
             }
 
-            map.put(value, new SoftReference<NinePatch>(ninePatch));
+            map.put(value, new SoftReference<NinePatchChunk>(ninePatch));
         } else {
-            sFramework9PatchCache.put(value, new SoftReference<NinePatch>(ninePatch));
+            sFramework9PatchCache.put(value, new SoftReference<NinePatchChunk>(ninePatch));
         }
     }
 
@@ -436,24 +436,24 @@
     }
 
     /**
-     * Returns the 9 patch for a specific path, from a specific project cache, or from the
+     * Returns the 9 patch chunk for a specific path, from a specific project cache, or from the
      * framework cache.
      * @param value the path of the 9 patch
      * @param projectKey the key of the project, or null to query the framework cache.
      * @return the cached 9 patch or null if not found.
      */
-    public static NinePatch getCached9Patch(String value, Object projectKey) {
+    public static NinePatchChunk getCached9Patch(String value, Object projectKey) {
         if (projectKey != null) {
-            Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey);
+            Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
 
             if (map != null) {
-                SoftReference<NinePatch> ref = map.get(value);
+                SoftReference<NinePatchChunk> ref = map.get(value);
                 if (ref != null) {
                     return ref.get();
                 }
             }
         } else {
-            SoftReference<NinePatch> ref = sFramework9PatchCache.get(value);
+            SoftReference<NinePatchChunk> ref = sFramework9PatchCache.get(value);
             if (ref != null) {
                 return ref.get();
             }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/NinePatchDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/NinePatchDrawable.java
deleted file mode 100644
index 4efa631..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/NinePatchDrawable.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.android;
-
-import com.android.ninepatch.NinePatch;
-
-import android.graphics.Canvas;
-import android.graphics.Canvas_Delegate;
-import android.graphics.ColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-
-public class NinePatchDrawable extends Drawable {
-
-    private NinePatch m9Patch;
-
-    public NinePatchDrawable(NinePatch ninePatch) {
-        m9Patch = ninePatch;
-    }
-
-    @Override
-    public int getMinimumWidth() {
-        return m9Patch.getWidth();
-    }
-
-    @Override
-    public int getMinimumHeight() {
-        return m9Patch.getHeight();
-    }
-
-    /**
-     * Return the intrinsic width of the underlying drawable object.  Returns
-     * -1 if it has no intrinsic width, such as with a solid color.
-     */
-    @Override
-    public int getIntrinsicWidth() {
-        return m9Patch.getWidth();
-    }
-
-    /**
-     * Return the intrinsic height of the underlying drawable object. Returns
-     * -1 if it has no intrinsic height, such as with a solid color.
-     */
-    @Override
-    public int getIntrinsicHeight() {
-        return m9Patch.getHeight();
-    }
-
-    /**
-     * Return in padding the insets suggested by this Drawable for placing
-     * content inside the drawable's bounds. Positive values move toward the
-     * center of the Drawable (set Rect.inset). Returns true if this drawable
-     * actually has a padding, else false. When false is returned, the padding
-     * is always set to 0.
-     */
-    @Override
-    public boolean getPadding(Rect padding) {
-        int[] padd = new int[4];
-        m9Patch.getPadding(padd);
-        padding.left = padd[0];
-        padding.top = padd[1];
-        padding.right = padd[2];
-        padding.bottom = padd[3];
-        return true;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        Rect r = getBounds();
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
-        m9Patch.draw(canvasDelegate.getGraphics2d(), r.left, r.top, r.width(), r.height());
-
-        return;
-    }
-
-
-    // ----------- Not implemented methods ---------------
-
-
-    @Override
-    public int getOpacity() {
-        // FIXME
-        return 0xFF;
-    }
-
-    @Override
-    public void setAlpha(int arg0) {
-        // FIXME !
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter arg0) {
-        // FIXME
-    }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
index 2e3f9a8..f7d249e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
@@ -313,10 +313,12 @@
 
             // create an Android bitmap around the BufferedImage
             Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
+                    true /*isMutable*/,
                     Density.getEnum(mParams.getDensity()));
 
             // create a Canvas around the Android bitmap
             Canvas canvas = new Canvas(bitmap);
+            canvas.setDensity(mParams.getDensity());
 
             // to set the logger, get the native delegate
             Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 3e506b8..ceb8a0d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -22,8 +22,8 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.android.NinePatchDrawable;
 import com.android.ninepatch.NinePatch;
+import com.android.ninepatch.NinePatchChunk;
 
 import org.kxml2.io.KXmlParser;
 import org.xmlpull.v1.XmlPullParser;
@@ -31,9 +31,12 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Bitmap_Delegate;
+import android.graphics.NinePatch_Delegate;
+import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
 import android.util.TypedValue;
 
 import java.io.File;
@@ -121,15 +124,38 @@
         if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
             File file = new File(stringValue);
             if (file.isFile()) {
-                NinePatch ninePatch = Bridge.getCached9Patch(stringValue,
+                // see if we still have both the chunk and the bitmap in the caches
+                NinePatchChunk chunk = Bridge.getCached9Patch(stringValue,
+                        isFramework ? null : context.getProjectKey());
+                Bitmap bitmap = Bridge.getCachedBitmap(stringValue,
                         isFramework ? null : context.getProjectKey());
 
-                if (ninePatch == null) {
+                // if either chunk or bitmap is null, then we reload the 9-patch file.
+                if (chunk == null || bitmap == null) {
                     try {
-                        ninePatch = NinePatch.load(file.toURL(), false /* convert */);
+                        NinePatch ninePatch = NinePatch.load(file.toURL(), false /* convert */);
+                        if (ninePatch != null) {
+                            if (chunk == null) {
+                                chunk = ninePatch.getChunk();
 
-                        Bridge.setCached9Patch(stringValue, ninePatch,
-                                isFramework ? null : context.getProjectKey());
+                                Bridge.setCached9Patch(stringValue, chunk,
+                                        isFramework ? null : context.getProjectKey());
+                            }
+
+                            if (bitmap == null) {
+                                Density density = Density.MEDIUM;
+                                if (value instanceof IDensityBasedResourceValue) {
+                                    density = ((IDensityBasedResourceValue)value).getDensity();
+                                }
+
+                                bitmap = Bitmap_Delegate.createBitmap(ninePatch.getImage(),
+                                        false /*isMutable*/,
+                                        density);
+
+                                Bridge.setCachedBitmap(stringValue, bitmap,
+                                        isFramework ? null : context.getProjectKey());
+                            }
+                        }
                     } catch (MalformedURLException e) {
                         // URL is wrong, we'll return null below
                     } catch (IOException e) {
@@ -137,8 +163,13 @@
                     }
                 }
 
-                if (ninePatch != null) {
-                    return new NinePatchDrawable(ninePatch);
+                if (chunk != null && bitmap != null) {
+                    int[] padding = chunk.getPadding();
+                    Rect paddingRect = new Rect(padding[0], padding[1], padding[2], padding[3]);
+
+                    return new NinePatchDrawable(context.getResources(), bitmap,
+                            NinePatch_Delegate.serialize(chunk),
+                            paddingRect, null);
                 }
             }
 
@@ -174,29 +205,17 @@
                             isFramework ? null : context.getProjectKey());
 
                     if (bitmap == null) {
-                        // always create the cache copy in the original density.
-                        bitmap = Bitmap_Delegate.createBitmap(bmpFile, Density.MEDIUM);
+                        Density density = Density.MEDIUM;
+                        if (value instanceof IDensityBasedResourceValue) {
+                            density = ((IDensityBasedResourceValue)value).getDensity();
+                        }
+
+                        bitmap = Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/,
+                                density);
                         Bridge.setCachedBitmap(stringValue, bitmap,
                                 isFramework ? null : context.getProjectKey());
                     }
 
-                    try {
-                        if (value instanceof IDensityBasedResourceValue) {
-                            Density density = ((IDensityBasedResourceValue)value).getDensity();
-                            if (density != Density.MEDIUM) {
-                                // create a copy of the bitmap
-                                bitmap = Bitmap.createBitmap(bitmap);
-
-                                // apply the density
-                                bitmap.setDensity(density.getValue());
-                            }
-                        }
-                    } catch (NoClassDefFoundError error) {
-                        // look like we're running in an older version of ADT that doesn't include
-                        // the new layoutlib_api. Let's just ignore this, the drawing will just be
-                        // wrong.
-                    }
-
                     return new BitmapDrawable(context.getResources(), bitmap);
                 } catch (IOException e) {
                     // we'll return null below
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
index 23351ab..a3219e7 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
@@ -48,5 +48,4 @@
         assertEquals(36, mPatch.getWidth());
         assertEquals(25, mPatch.getHeight());
     }
-
 }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index b9c71133..bb2e6b3 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -109,6 +109,7 @@
         "android.graphics.DashPathEffect",
         "android.graphics.LinearGradient",
         "android.graphics.Matrix",
+        "android.graphics.NinePatch",
         "android.graphics.Paint",
         "android.graphics.PathEffect",
         "android.graphics.PorterDuffXfermode",
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 90abd02..7e3df1a 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1529,7 +1529,7 @@
     }
 
     void setNetworkAvailable(boolean available) {
-        sendMessage(CMD_SET_NETWORK_AVAILABLE, available ? 1 : 0);
+        sendMessage(obtainMessage(CMD_SET_NETWORK_AVAILABLE, available ? 1 : 0, 0));
     }
 
     /********************************************************