Merge "Some cleanup of Proxy class."
diff --git a/Android.mk b/Android.mk
index 9d7906e..50e05d9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -39,9 +39,6 @@
        core/java/android/webkit/EventLogTags.logtags \
        telephony/java/com/android/internal/telephony/EventLogTags.logtags \
 
-# RenderScript files for internal widgets
-LOCAL_SRC_FILES += $(call all-renderscript-files-under, core/java/com/android/internal/widget)
-
 # The following filters out code we are temporarily not including at all.
 # TODO: Move AWT and beans (and associated harmony code) back into libcore.
 # TODO: Maybe remove javax.microedition entirely?
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 50bee9c..e5f1f90 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -79,6 +79,8 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/storage/*)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content/IClipboard.P)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/ITelephonyRegistry.P)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates)
+$(call add-clean-step, rm -rf out/target/common/docs/api-stubs*)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/9.xml b/api/9.xml
index 08f86e1..da0b71b 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -793,17 +793,6 @@
  visibility="public"
 >
 </field>
-<field name="READ_OWNER_DATA"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.permission.READ_OWNER_DATA&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="READ_PHONE_STATE"
  type="java.lang.String"
  transient="false"
@@ -1233,17 +1222,6 @@
  visibility="public"
 >
 </field>
-<field name="WRITE_OWNER_DATA"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.permission.WRITE_OWNER_DATA&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="WRITE_SECURE_SETTINGS"
  type="java.lang.String"
  transient="false"
@@ -15867,17 +15845,6 @@
  visibility="public"
 >
 </method>
-<method name="getInstanceCount"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getIntent"
  return="android.content.Intent"
  abstract="false"
diff --git a/api/current.xml b/api/current.xml
index 0307bff..bfbeaa2 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -712,7 +712,7 @@
  value="&quot;android.permission.PERSISTENT_ACTIVITY&quot;"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -793,17 +793,6 @@
  visibility="public"
 >
 </field>
-<field name="READ_OWNER_DATA"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.permission.READ_OWNER_DATA&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="READ_PHONE_STATE"
  type="java.lang.String"
  transient="false"
@@ -1233,17 +1222,6 @@
  visibility="public"
 >
 </field>
-<field name="WRITE_OWNER_DATA"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.permission.WRITE_OWNER_DATA&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="WRITE_SECURE_SETTINGS"
  type="java.lang.String"
  transient="false"
@@ -2363,6 +2341,17 @@
  visibility="public"
 >
 </field>
+<field name="animateLayoutChanges"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843570"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="animateOnClick"
  type="int"
  transient="false"
@@ -4211,6 +4200,17 @@
  visibility="public"
 >
 </field>
+<field name="filterTouchesWhenObscured"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843460"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="finishOnCloseSystemDialogs"
  type="int"
  transient="false"
@@ -6169,50 +6169,6 @@
  visibility="public"
 >
 </field>
-<field name="kraken_resource_pad61"
- type="int"
- transient="false"
- volatile="false"
- value="16843460"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad62"
- type="int"
- transient="false"
- volatile="false"
- value="16843459"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad63"
- type="int"
- transient="false"
- volatile="false"
- value="16843458"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad64"
- type="int"
- transient="false"
- volatile="false"
- value="16843457"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="kraken_resource_pad7"
  type="int"
  transient="false"
@@ -8919,6 +8875,17 @@
  visibility="public"
 >
 </field>
+<field name="spinnerMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843569"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="spinnerStyle"
  type="int"
  transient="false"
@@ -16482,6 +16449,50 @@
  visibility="public"
 >
 </field>
+<field name="TextAppearance_StatusBar_EventContent"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973927"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TextAppearance_StatusBar_EventContent_Title"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973928"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TextAppearance_StatusBar_Icon"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973926"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TextAppearance_StatusBar_Title"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973925"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TextAppearance_Theme"
  type="int"
  transient="false"
@@ -17824,50 +17835,6 @@
  visibility="public"
 >
 </field>
-<field name="kraken_resource_pad41"
- type="int"
- transient="false"
- volatile="false"
- value="16973928"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad42"
- type="int"
- transient="false"
- volatile="false"
- value="16973927"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad43"
- type="int"
- transient="false"
- volatile="false"
- value="16973926"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad44"
- type="int"
- transient="false"
- volatile="false"
- value="16973925"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="kraken_resource_pad5"
  type="int"
  transient="false"
@@ -19810,7 +19777,7 @@
 </package>
 <package name="android.animation"
 >
-<class name="Animatable"
+<class name="Animator"
  extends="java.lang.Object"
  abstract="true"
  static="false"
@@ -19820,8 +19787,8 @@
 >
 <implements name="java.lang.Cloneable">
 </implements>
-<constructor name="Animatable"
- type="android.animation.Animatable"
+<constructor name="Animator"
+ type="android.animation.Animator"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -19838,7 +19805,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="listener" type="android.animation.Animatable.AnimatableListener">
+<parameter name="listener" type="android.animation.Animator.AnimatorListener">
 </parameter>
 </method>
 <method name="cancel"
@@ -19853,7 +19820,7 @@
 >
 </method>
 <method name="clone"
- return="android.animation.Animatable"
+ return="android.animation.Animator"
  abstract="false"
  native="false"
  synchronized="false"
@@ -19862,8 +19829,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<exception name="CloneNotSupportedException" type="java.lang.CloneNotSupportedException">
-</exception>
 </method>
 <method name="end"
  return="void"
@@ -19876,8 +19841,19 @@
  visibility="public"
 >
 </method>
+<method name="getDuration"
+ return="long"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getListeners"
- return="java.util.ArrayList&lt;android.animation.Animatable.AnimatableListener&gt;"
+ return="java.util.ArrayList&lt;android.animation.Animator.AnimatorListener&gt;"
  abstract="false"
  native="false"
  synchronized="false"
@@ -19887,6 +19863,17 @@
  visibility="public"
 >
 </method>
+<method name="getStartDelay"
+ return="long"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isRunning"
  return="boolean"
  abstract="true"
@@ -19919,9 +19906,83 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="listener" type="android.animation.Animatable.AnimatableListener">
+<parameter name="listener" type="android.animation.Animator.AnimatorListener">
 </parameter>
 </method>
+<method name="setDuration"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setInterpolator"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="android.view.animation.Interpolator">
+</parameter>
+</method>
+<method name="setStartDelay"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startDelay" type="long">
+</parameter>
+</method>
+<method name="setTarget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="java.lang.Object">
+</parameter>
+</method>
+<method name="setupEndValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setupStartValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="start"
  return="void"
  abstract="false"
@@ -19934,7 +19995,7 @@
 >
 </method>
 </class>
-<interface name="Animatable.AnimatableListener"
+<interface name="Animator.AnimatorListener"
  abstract="true"
  static="true"
  final="false"
@@ -19951,7 +20012,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="animation" type="android.animation.Animatable">
+<parameter name="animation" type="android.animation.Animator">
 </parameter>
 </method>
 <method name="onAnimationEnd"
@@ -19964,7 +20025,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="animation" type="android.animation.Animatable">
+<parameter name="animation" type="android.animation.Animator">
 </parameter>
 </method>
 <method name="onAnimationRepeat"
@@ -19977,7 +20038,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="animation" type="android.animation.Animatable">
+<parameter name="animation" type="android.animation.Animator">
 </parameter>
 </method>
 <method name="onAnimationStart"
@@ -19990,11 +20051,11 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="animation" type="android.animation.Animatable">
+<parameter name="animation" type="android.animation.Animator">
 </parameter>
 </method>
 </interface>
-<class name="AnimatableInflater"
+<class name="AnimatorInflater"
  extends="java.lang.Object"
  abstract="false"
  static="false"
@@ -20002,16 +20063,16 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<constructor name="AnimatableInflater"
- type="android.animation.AnimatableInflater"
+<constructor name="AnimatorInflater"
+ type="android.animation.AnimatorInflater"
  static="false"
  final="false"
  deprecated="not deprecated"
  visibility="public"
 >
 </constructor>
-<method name="loadAnimatable"
- return="android.animation.Animatable"
+<method name="loadAnimator"
+ return="android.animation.Animator"
  abstract="false"
  native="false"
  synchronized="false"
@@ -20028,7 +20089,7 @@
 </exception>
 </method>
 </class>
-<class name="AnimatableListenerAdapter"
+<class name="AnimatorListenerAdapter"
  extends="java.lang.Object"
  abstract="true"
  static="false"
@@ -20036,10 +20097,10 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="android.animation.Animatable.AnimatableListener">
+<implements name="android.animation.Animator.AnimatorListener">
 </implements>
-<constructor name="AnimatableListenerAdapter"
- type="android.animation.AnimatableListenerAdapter"
+<constructor name="AnimatorListenerAdapter"
+ type="android.animation.AnimatorListenerAdapter"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -20056,7 +20117,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="animation" type="android.animation.Animatable">
+<parameter name="animation" type="android.animation.Animator">
 </parameter>
 </method>
 <method name="onAnimationEnd"
@@ -20069,7 +20130,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="animation" type="android.animation.Animatable">
+<parameter name="animation" type="android.animation.Animator">
 </parameter>
 </method>
 <method name="onAnimationRepeat"
@@ -20082,7 +20143,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="animation" type="android.animation.Animatable">
+<parameter name="animation" type="android.animation.Animator">
 </parameter>
 </method>
 <method name="onAnimationStart"
@@ -20095,28 +20156,1137 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="animation" type="android.animation.Animatable">
+<parameter name="animation" type="android.animation.Animator">
 </parameter>
 </method>
 </class>
-<class name="Animator"
- extends="android.animation.Animatable"
+<class name="AnimatorSet"
+ extends="android.animation.Animator"
  abstract="false"
  static="false"
- final="false"
+ final="true"
  deprecated="not deprecated"
  visibility="public"
 >
-<constructor name="Animator"
- type="android.animation.Animator"
+<constructor name="AnimatorSet"
+ type="android.animation.AnimatorSet"
  static="false"
  final="false"
  deprecated="not deprecated"
  visibility="public"
 >
 </constructor>
-<constructor name="Animator"
- type="android.animation.Animator"
+<method name="getChildAnimations"
+ return="java.util.ArrayList&lt;android.animation.Animator&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDuration"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStartDelay"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isRunning"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="play"
+ return="android.animation.AnimatorSet.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="anim" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="playSequentially"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="items" type="android.animation.Animator...">
+</parameter>
+</method>
+<method name="playTogether"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="items" type="android.animation.Animator...">
+</parameter>
+</method>
+<method name="setDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setInterpolator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="interpolator" type="android.view.animation.Interpolator">
+</parameter>
+</method>
+<method name="setStartDelay"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startDelay" type="long">
+</parameter>
+</method>
+</class>
+<class name="AnimatorSet.Builder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="after"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="anim" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="after"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="delay" type="long">
+</parameter>
+</method>
+<method name="before"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="anim" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="with"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="anim" type="android.animation.Animator">
+</parameter>
+</method>
+</class>
+<class name="DoubleEvaluator"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.animation.TypeEvaluator">
+</implements>
+<constructor name="DoubleEvaluator"
+ type="android.animation.DoubleEvaluator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="evaluate"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="startValue" type="java.lang.Object">
+</parameter>
+<parameter name="endValue" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<class name="FloatEvaluator"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.animation.TypeEvaluator">
+</implements>
+<constructor name="FloatEvaluator"
+ type="android.animation.FloatEvaluator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="evaluate"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="startValue" type="java.lang.Object">
+</parameter>
+<parameter name="endValue" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<class name="IntEvaluator"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.animation.TypeEvaluator">
+</implements>
+<constructor name="IntEvaluator"
+ type="android.animation.IntEvaluator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="evaluate"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="startValue" type="java.lang.Object">
+</parameter>
+<parameter name="endValue" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<class name="Keyframe"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.lang.Cloneable">
+</implements>
+<constructor name="Keyframe"
+ type="android.animation.Keyframe"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="value" type="java.lang.Object">
+</parameter>
+</constructor>
+<constructor name="Keyframe"
+ type="android.animation.Keyframe"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="value" type="java.lang.Float">
+</parameter>
+</constructor>
+<constructor name="Keyframe"
+ type="android.animation.Keyframe"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="value" type="java.lang.Integer">
+</parameter>
+</constructor>
+<constructor name="Keyframe"
+ type="android.animation.Keyframe"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="value" type="java.lang.Double">
+</parameter>
+</constructor>
+<constructor name="Keyframe"
+ type="android.animation.Keyframe"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="value" type="int">
+</parameter>
+</constructor>
+<constructor name="Keyframe"
+ type="android.animation.Keyframe"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="value" type="float">
+</parameter>
+</constructor>
+<constructor name="Keyframe"
+ type="android.animation.Keyframe"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="value" type="double">
+</parameter>
+</constructor>
+<method name="clone"
+ return="android.animation.Keyframe"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFraction"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInterpolator"
+ return="android.view.animation.Interpolator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getType"
+ return="java.lang.Class"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getValue"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setFraction"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+</method>
+<method name="setInterpolator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="interpolator" type="android.view.animation.Interpolator">
+</parameter>
+</method>
+<method name="setValue"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<class name="LayoutTransition"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="LayoutTransition"
+ type="android.animation.LayoutTransition"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addTransitionListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.animation.LayoutTransition.TransitionListener">
+</parameter>
+</method>
+<method name="childAdd"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parent" type="android.view.ViewGroup">
+</parameter>
+<parameter name="child" type="android.view.View">
+</parameter>
+</method>
+<method name="childRemove"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parent" type="android.view.ViewGroup">
+</parameter>
+<parameter name="child" type="android.view.View">
+</parameter>
+</method>
+<method name="getAnimator"
+ return="android.animation.Animator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="getDuration"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="getInterpolator"
+ return="android.view.animation.Interpolator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="getStagger"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="getStartDelay"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="getTransitionListeners"
+ return="java.util.List&lt;android.animation.LayoutTransition.TransitionListener&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeTransitionListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.animation.LayoutTransition.TransitionListener">
+</parameter>
+</method>
+<method name="setAnimator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+<parameter name="animator" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="setDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setInterpolator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+<parameter name="interpolator" type="android.view.animation.Interpolator">
+</parameter>
+</method>
+<method name="setStagger"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setStartDelay"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+<parameter name="delay" type="long">
+</parameter>
+</method>
+<field name="APPEARING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANGE_APPEARING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANGE_DISAPPEARING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISAPPEARING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="LayoutTransition.TransitionListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="endTransition"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transition" type="android.animation.LayoutTransition">
+</parameter>
+<parameter name="container" type="android.view.ViewGroup">
+</parameter>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="startTransition"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transition" type="android.animation.LayoutTransition">
+</parameter>
+<parameter name="container" type="android.view.ViewGroup">
+</parameter>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+</interface>
+<class name="ObjectAnimator"
+ extends="android.animation.ValueAnimator"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ObjectAnimator"
+ type="android.animation.ObjectAnimator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="ObjectAnimator"
+ type="android.animation.ObjectAnimator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="long">
+</parameter>
+<parameter name="target" type="java.lang.Object">
+</parameter>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+<parameter name="values" type="T...">
+</parameter>
+</constructor>
+<constructor name="ObjectAnimator"
+ type="android.animation.ObjectAnimator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="long">
+</parameter>
+<parameter name="target" type="java.lang.Object">
+</parameter>
+<parameter name="values" type="android.animation.PropertyValuesHolder...">
+</parameter>
+</constructor>
+<method name="getPropertyName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTarget"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setPropertyName"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+</method>
+</class>
+<class name="PropertyValuesHolder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.lang.Cloneable">
+</implements>
+<constructor name="PropertyValuesHolder"
+ type="android.animation.PropertyValuesHolder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="T...">
+</parameter>
+</constructor>
+<constructor name="PropertyValuesHolder"
+ type="android.animation.PropertyValuesHolder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+<parameter name="values" type="T...">
+</parameter>
+</constructor>
+<method name="clone"
+ return="android.animation.PropertyValuesHolder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGetter"
+ return="java.lang.reflect.Method"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPropertyName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSetter"
+ return="java.lang.reflect.Method"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setEvaluator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="evaluator" type="android.animation.TypeEvaluator">
+</parameter>
+</method>
+<method name="setGetter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="getter" type="java.lang.reflect.Method">
+</parameter>
+</method>
+<method name="setPropertyName"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+</method>
+<method name="setSetter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="setter" type="java.lang.reflect.Method">
+</parameter>
+</method>
+<method name="setValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="T...">
+</parameter>
+</method>
+</class>
+<class name="RGBEvaluator"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.animation.TypeEvaluator">
+</implements>
+<constructor name="RGBEvaluator"
+ type="android.animation.RGBEvaluator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="evaluate"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="startValue" type="java.lang.Object">
+</parameter>
+<parameter name="endValue" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<interface name="TypeEvaluator"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="evaluate"
+ return="java.lang.Object"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="startValue" type="java.lang.Object">
+</parameter>
+<parameter name="endValue" type="java.lang.Object">
+</parameter>
+</method>
+</interface>
+<class name="ValueAnimator"
+ extends="android.animation.Animator"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ValueAnimator"
+ type="android.animation.ValueAnimator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="ValueAnimator"
+ type="android.animation.ValueAnimator"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -20137,7 +21307,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="listener" type="android.animation.Animator.AnimatorUpdateListener">
+<parameter name="listener" type="android.animation.ValueAnimator.AnimatorUpdateListener">
 </parameter>
 </method>
 <method name="getAnimatedValue"
@@ -20241,6 +21411,17 @@
  visibility="public"
 >
 </method>
+<method name="getValues"
+ return="android.animation.PropertyValuesHolder[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isRunning"
  return="boolean"
  abstract="false"
@@ -20262,7 +21443,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="listener" type="android.animation.Animator.AnimatorUpdateListener">
+<parameter name="listener" type="android.animation.ValueAnimator.AnimatorUpdateListener">
 </parameter>
 </method>
 <method name="reverse"
@@ -20440,7 +21621,7 @@
 >
 </field>
 </class>
-<interface name="Animator.AnimatorUpdateListener"
+<interface name="ValueAnimator.AnimatorUpdateListener"
  abstract="true"
  static="true"
  final="false"
@@ -20457,692 +21638,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="animation" type="android.animation.Animator">
-</parameter>
-</method>
-</interface>
-<class name="DoubleEvaluator"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.animation.TypeEvaluator">
-</implements>
-<constructor name="DoubleEvaluator"
- type="android.animation.DoubleEvaluator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="evaluate"
- return="java.lang.Object"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fraction" type="float">
-</parameter>
-<parameter name="startValue" type="java.lang.Object">
-</parameter>
-<parameter name="endValue" type="java.lang.Object">
-</parameter>
-</method>
-</class>
-<class name="FloatEvaluator"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.animation.TypeEvaluator">
-</implements>
-<constructor name="FloatEvaluator"
- type="android.animation.FloatEvaluator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="evaluate"
- return="java.lang.Object"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fraction" type="float">
-</parameter>
-<parameter name="startValue" type="java.lang.Object">
-</parameter>
-<parameter name="endValue" type="java.lang.Object">
-</parameter>
-</method>
-</class>
-<class name="IntEvaluator"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.animation.TypeEvaluator">
-</implements>
-<constructor name="IntEvaluator"
- type="android.animation.IntEvaluator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="evaluate"
- return="java.lang.Object"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fraction" type="float">
-</parameter>
-<parameter name="startValue" type="java.lang.Object">
-</parameter>
-<parameter name="endValue" type="java.lang.Object">
-</parameter>
-</method>
-</class>
-<class name="Keyframe"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Keyframe"
- type="android.animation.Keyframe"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fraction" type="float">
-</parameter>
-<parameter name="value" type="java.lang.Object">
-</parameter>
-</constructor>
-<constructor name="Keyframe"
- type="android.animation.Keyframe"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fraction" type="float">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-</constructor>
-<constructor name="Keyframe"
- type="android.animation.Keyframe"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fraction" type="float">
-</parameter>
-<parameter name="value" type="float">
-</parameter>
-</constructor>
-<constructor name="Keyframe"
- type="android.animation.Keyframe"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fraction" type="float">
-</parameter>
-<parameter name="value" type="double">
-</parameter>
-</constructor>
-<method name="getFraction"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getInterpolator"
- return="android.view.animation.Interpolator"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getType"
- return="java.lang.Class"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getValue"
- return="java.lang.Object"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setFraction"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fraction" type="float">
-</parameter>
-</method>
-<method name="setInterpolator"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="interpolator" type="android.view.animation.Interpolator">
-</parameter>
-</method>
-<method name="setValue"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="value" type="java.lang.Object">
-</parameter>
-</method>
-</class>
-<class name="PropertyAnimator"
- extends="android.animation.Animator"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="PropertyAnimator"
- type="android.animation.PropertyAnimator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="PropertyAnimator"
- type="android.animation.PropertyAnimator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="duration" type="long">
-</parameter>
-<parameter name="target" type="java.lang.Object">
-</parameter>
-<parameter name="propertyName" type="java.lang.String">
-</parameter>
-<parameter name="values" type="T...">
-</parameter>
-</constructor>
-<constructor name="PropertyAnimator"
- type="android.animation.PropertyAnimator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="duration" type="long">
-</parameter>
-<parameter name="target" type="java.lang.Object">
-</parameter>
-<parameter name="values" type="android.animation.PropertyValuesHolder...">
-</parameter>
-</constructor>
-<method name="getPropertyName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getTarget"
- return="java.lang.Object"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setPropertyName"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="propertyName" type="java.lang.String">
-</parameter>
-</method>
-<method name="setTarget"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="target" type="java.lang.Object">
-</parameter>
-</method>
-</class>
-<class name="PropertyValuesHolder"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="PropertyValuesHolder"
- type="android.animation.PropertyValuesHolder"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="values" type="T...">
-</parameter>
-</constructor>
-<constructor name="PropertyValuesHolder"
- type="android.animation.PropertyValuesHolder"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="propertyName" type="java.lang.String">
-</parameter>
-<parameter name="values" type="T...">
-</parameter>
-</constructor>
-<method name="getGetter"
- return="java.lang.reflect.Method"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getPropertyName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getSetter"
- return="java.lang.reflect.Method"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setEvaluator"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="evaluator" type="android.animation.TypeEvaluator">
-</parameter>
-</method>
-<method name="setGetter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="getter" type="java.lang.reflect.Method">
-</parameter>
-</method>
-<method name="setPropertyName"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="propertyName" type="java.lang.String">
-</parameter>
-</method>
-<method name="setSetter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="setter" type="java.lang.reflect.Method">
-</parameter>
-</method>
-<method name="setValues"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="values" type="T...">
-</parameter>
-</method>
-</class>
-<class name="RGBEvaluator"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.animation.TypeEvaluator">
-</implements>
-<constructor name="RGBEvaluator"
- type="android.animation.RGBEvaluator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="evaluate"
- return="java.lang.Object"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fraction" type="float">
-</parameter>
-<parameter name="startValue" type="java.lang.Object">
-</parameter>
-<parameter name="endValue" type="java.lang.Object">
-</parameter>
-</method>
-</class>
-<class name="Sequencer"
- extends="android.animation.Animatable"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Sequencer"
- type="android.animation.Sequencer"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="getChildAnimations"
- return="java.util.ArrayList&lt;android.animation.Animatable&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isRunning"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="play"
- return="android.animation.Sequencer.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="anim" type="android.animation.Animatable">
-</parameter>
-</method>
-<method name="playSequentially"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="sequenceItems" type="android.animation.Animatable...">
-</parameter>
-</method>
-<method name="playTogether"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="sequenceItems" type="android.animation.Animatable...">
-</parameter>
-</method>
-<method name="setTarget"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="target" type="java.lang.Object">
-</parameter>
-</method>
-</class>
-<class name="Sequencer.Builder"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="after"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="anim" type="android.animation.Animatable">
-</parameter>
-</method>
-<method name="after"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="delay" type="long">
-</parameter>
-</method>
-<method name="before"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="anim" type="android.animation.Animatable">
-</parameter>
-</method>
-<method name="with"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="anim" type="android.animation.Animatable">
-</parameter>
-</method>
-</class>
-<interface name="TypeEvaluator"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="evaluate"
- return="java.lang.Object"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fraction" type="float">
-</parameter>
-<parameter name="startValue" type="java.lang.Object">
-</parameter>
-<parameter name="endValue" type="java.lang.Object">
+<parameter name="animation" type="android.animation.ValueAnimator">
 </parameter>
 </method>
 </interface>
@@ -21178,6 +21674,21 @@
 <parameter name="tab" type="android.app.ActionBar.Tab">
 </parameter>
 </method>
+<method name="addTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+</method>
 <method name="getCustomNavigationView"
  return="android.view.View"
  abstract="true"
@@ -21233,6 +21744,17 @@
  visibility="public"
 >
 </method>
+<method name="getSelectedTab"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getSubtitle"
  return="java.lang.CharSequence"
  abstract="true"
@@ -21266,21 +21788,6 @@
  visibility="public"
 >
 </method>
-<method name="insertTab"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="tab" type="android.app.ActionBar.Tab">
-</parameter>
-<parameter name="position" type="int">
-</parameter>
-</method>
 <method name="isShowing"
  return="boolean"
  abstract="true"
@@ -21451,62 +21958,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="title" type="java.lang.CharSequence">
-</parameter>
-<parameter name="subtitle" type="java.lang.CharSequence">
-</parameter>
-</method>
-<method name="setStandardNavigationMode"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="titleResId" type="int">
-</parameter>
-<parameter name="subtitleResId" type="int">
-</parameter>
-</method>
-<method name="setStandardNavigationMode"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="title" type="java.lang.CharSequence">
-</parameter>
-</method>
-<method name="setStandardNavigationMode"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="titleResId" type="int">
-</parameter>
-</method>
-<method name="setStandardNavigationMode"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 </method>
 <method name="setSubtitle"
  return="void"
@@ -21545,19 +21996,6 @@
  visibility="public"
 >
 </method>
-<method name="setTabNavigationMode"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="containerViewId" type="int">
-</parameter>
-</method>
 <method name="setTitle"
  return="void"
  abstract="true"
@@ -21701,8 +22139,8 @@
  visibility="public"
 >
 </constructor>
-<method name="getFragment"
- return="android.app.Fragment"
+<method name="getCustomView"
+ return="android.view.View"
  abstract="true"
  native="false"
  synchronized="false"
@@ -21734,6 +22172,17 @@
  visibility="public"
 >
 </method>
+<method name="getTag"
+ return="java.lang.Object"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getText"
  return="java.lang.CharSequence"
  abstract="true"
@@ -21756,7 +22205,7 @@
  visibility="public"
 >
 </method>
-<method name="setFragment"
+<method name="setCustomView"
  return="void"
  abstract="true"
  native="false"
@@ -21766,7 +22215,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fragment" type="android.app.Fragment">
+<parameter name="view" type="android.view.View">
 </parameter>
 </method>
 <method name="setIcon"
@@ -21782,6 +22231,32 @@
 <parameter name="icon" type="android.graphics.drawable.Drawable">
 </parameter>
 </method>
+<method name="setTabListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.ActionBar.TabListener">
+</parameter>
+</method>
+<method name="setTag"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="obj" type="java.lang.Object">
+</parameter>
+</method>
 <method name="setText"
  return="void"
  abstract="true"
@@ -21807,6 +22282,44 @@
 >
 </field>
 </class>
+<interface name="ActionBar.TabListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onTabSelected"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="ft" type="android.app.FragmentTransaction">
+</parameter>
+</method>
+<method name="onTabUnselected"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="ft" type="android.app.FragmentTransaction">
+</parameter>
+</method>
+</interface>
 <class name="Activity"
  extends="android.view.ContextThemeWrapper"
  abstract="false"
@@ -22131,17 +22644,6 @@
  visibility="public"
 >
 </method>
-<method name="getInstanceCount"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getIntent"
  return="android.content.Intent"
  abstract="false"
@@ -23415,7 +23917,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="isPersistent" type="boolean">
@@ -24666,17 +25168,6 @@
  visibility="public"
 >
 </field>
-<field name="FLAG_HEAVY_WEIGHT"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="IMPORTANCE_BACKGROUND"
  type="int"
  transient="false"
@@ -24787,16 +25278,6 @@
  visibility="public"
 >
 </field>
-<field name="flags"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="importance"
  type="int"
  transient="false"
@@ -28245,8 +28726,8 @@
 <parameter name="savedInstanceState" type="android.os.Bundle">
 </parameter>
 </method>
-<method name="onCreateAnimatable"
- return="android.animation.Animatable"
+<method name="onCreateAnimator"
+ return="android.animation.Animator"
  abstract="false"
  native="false"
  synchronized="false"
@@ -28829,6 +29310,17 @@
 <parameter name="fragment" type="android.app.Fragment">
 </parameter>
 </method>
+<method name="isEmpty"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="remove"
  return="android.app.FragmentTransaction"
  abstract="true"
@@ -55586,6 +56078,17 @@
  visibility="public"
 >
 </field>
+<field name="FEATURE_NFC"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.hardware.nfc&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FEATURE_SENSOR_ACCELEROMETER"
  type="java.lang.String"
  transient="false"
@@ -55597,6 +56100,17 @@
  visibility="public"
 >
 </field>
+<field name="FEATURE_SENSOR_BAROMETER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.hardware.sensor.barometer&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FEATURE_SENSOR_COMPASS"
  type="java.lang.String"
  transient="false"
@@ -55608,6 +56122,17 @@
  visibility="public"
 >
 </field>
+<field name="FEATURE_SENSOR_GYROSCOPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.hardware.sensor.gyroscope&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FEATURE_SENSOR_LIGHT"
  type="java.lang.String"
  transient="false"
@@ -55630,6 +56155,28 @@
  visibility="public"
 >
 </field>
+<field name="FEATURE_SIP"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.software.sip&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FEATURE_SIP_VOIP"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.software.sip.voip&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FEATURE_TELEPHONY"
  type="java.lang.String"
  transient="false"
@@ -55696,6 +56243,17 @@
  visibility="public"
 >
 </field>
+<field name="FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.hardware.touchscreen.multitouch.jazzhand&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FEATURE_WIFI"
  type="java.lang.String"
  transient="false"
@@ -139553,6 +140111,21 @@
 <parameter name="preferenceScreen" type="android.preference.PreferenceScreen">
 </parameter>
 </method>
+<method name="startPreferenceFragment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+<parameter name="push" type="boolean">
+</parameter>
+</method>
 <method name="startWithFragment"
  return="void"
  abstract="false"
@@ -146728,6 +147301,10 @@
 >
 <implements name="android.provider.BaseColumns">
 </implements>
+<implements name="android.provider.ContactsContract.ContactOptionsColumns">
+</implements>
+<implements name="android.provider.ContactsContract.ContactStatusColumns">
+</implements>
 <implements name="android.provider.ContactsContract.ContactsColumns">
 </implements>
 <field name="CONTENT_DIRECTORY"
@@ -146838,7 +147415,7 @@
 >
 <implements name="android.provider.BaseColumns">
 </implements>
-<implements name="android.provider.ContactsContract.DataColumns">
+<implements name="android.provider.ContactsContract.DataColumnsWithJoins">
 </implements>
 <field name="CONTENT_DIRECTORY"
  type="java.lang.String"
@@ -148623,7 +149200,7 @@
  value="1"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -173731,6 +174308,29 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
+<method name="formatDateRange"
+ return="java.util.Formatter"
+ 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="formatter" type="java.util.Formatter">
+</parameter>
+<parameter name="startMillis" type="long">
+</parameter>
+<parameter name="endMillis" type="long">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+<parameter name="timeZone" type="java.lang.String">
+</parameter>
+</method>
 <method name="formatDateTime"
  return="java.lang.String"
  abstract="false"
@@ -174224,7 +174824,7 @@
  value="8192"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -175339,16 +175939,6 @@
 <parameter name="cursorController" type="android.widget.TextView.CursorController">
 </parameter>
 </method>
-<field name="mCursorController"
- type="android.widget.TextView.CursorController"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
 </class>
 <class name="BaseKeyListener"
  extends="android.text.method.MetaKeyKeyListener"
@@ -191336,6 +191926,17 @@
  visibility="public"
 >
 </method>
+<method name="getFlags"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getHistoricalEventTime"
  return="long"
  abstract="false"
@@ -191973,6 +192574,8 @@
 </parameter>
 <parameter name="source" type="int">
 </parameter>
+<parameter name="flags" type="int">
+</parameter>
 </method>
 <method name="obtain"
  return="android.view.MotionEvent"
@@ -192429,6 +193032,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_WINDOW_IS_OBSCURED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="MotionEvent.PointerCoords"
  extends="java.lang.Object"
@@ -192765,6 +193379,28 @@
  visibility="public"
 >
 </method>
+<method name="getCurrentSpanX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrentSpanY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getEventTime"
  return="long"
  abstract="false"
@@ -192809,6 +193445,28 @@
  visibility="public"
 >
 </method>
+<method name="getPreviousSpanX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPreviousSpanY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getScaleFactor"
  return="float"
  abstract="false"
@@ -194388,6 +195046,19 @@
 <parameter name="focusableMode" type="int">
 </parameter>
 </method>
+<method name="addOnLayoutChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.view.View.OnLayoutChangeListener">
+</parameter>
+</method>
 <method name="addTouchables"
  return="void"
  abstract="false"
@@ -195162,6 +195833,17 @@
  visibility="public"
 >
 </method>
+<method name="getFilterTouchesWhenObscured"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getFocusables"
  return="java.util.ArrayList&lt;android.view.View&gt;"
  abstract="false"
@@ -195477,6 +196159,17 @@
  visibility="public"
 >
 </method>
+<method name="getOnLayoutChangeListeners"
+ return="java.util.List&lt;android.view.View.OnLayoutChangeListener&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getPaddingBottom"
  return="int"
  abstract="false"
@@ -196582,6 +197275,19 @@
 <parameter name="canvas" type="android.graphics.Canvas">
 </parameter>
 </method>
+<method name="onFilterTouchEventForSecurity"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
 <method name="onFinishInflate"
  return="void"
  abstract="false"
@@ -197081,6 +197787,19 @@
 <parameter name="action" type="java.lang.Runnable">
 </parameter>
 </method>
+<method name="removeOnLayoutChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.view.View.OnLayoutChangeListener">
+</parameter>
+</method>
 <method name="requestFocus"
  return="boolean"
  abstract="false"
@@ -197349,6 +198068,19 @@
 <parameter name="resid" type="int">
 </parameter>
 </method>
+<method name="setBottom"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bottom" type="int">
+</parameter>
+</method>
 <method name="setClickable"
  return="void"
  abstract="false"
@@ -197453,6 +198185,19 @@
 <parameter name="length" type="int">
 </parameter>
 </method>
+<method name="setFilterTouchesWhenObscured"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
 <method name="setFocusable"
  return="void"
  abstract="false"
@@ -197557,6 +198302,19 @@
 <parameter name="params" type="android.view.ViewGroup.LayoutParams">
 </parameter>
 </method>
+<method name="setLeft"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="left" type="int">
+</parameter>
+</method>
 <method name="setLongClickable"
  return="void"
  abstract="false"
@@ -197799,6 +198557,19 @@
 <parameter name="pressed" type="boolean">
 </parameter>
 </method>
+<method name="setRight"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="right" type="int">
+</parameter>
+</method>
 <method name="setRotation"
  return="void"
  abstract="false"
@@ -197983,6 +198754,19 @@
 <parameter name="tag" type="java.lang.Object">
 </parameter>
 </method>
+<method name="setTop"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="top" type="int">
+</parameter>
+</method>
 <method name="setTouchDelegate"
  return="void"
  abstract="false"
@@ -199045,6 +199829,43 @@
 </parameter>
 </method>
 </interface>
+<interface name="View.OnLayoutChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onLayoutChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="left" type="int">
+</parameter>
+<parameter name="top" type="int">
+</parameter>
+<parameter name="right" type="int">
+</parameter>
+<parameter name="bottom" type="int">
+</parameter>
+<parameter name="oldLeft" type="int">
+</parameter>
+<parameter name="oldTop" type="int">
+</parameter>
+<parameter name="oldRight" type="int">
+</parameter>
+<parameter name="oldBottom" type="int">
+</parameter>
+</method>
+</interface>
 <interface name="View.OnLongClickListener"
  abstract="true"
  static="true"
@@ -200301,6 +201122,17 @@
  visibility="public"
 >
 </method>
+<method name="getLayoutTransition"
+ return="android.animation.LayoutTransition"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getPersistentDrawingCache"
  return="int"
  abstract="false"
@@ -200873,6 +201705,19 @@
 <parameter name="animationListener" type="android.view.animation.Animation.AnimationListener">
 </parameter>
 </method>
+<method name="setLayoutTransition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transition" type="android.animation.LayoutTransition">
+</parameter>
+</method>
 <method name="setMotionEventSplittingEnabled"
  return="void"
  abstract="false"
@@ -232311,6 +233156,18 @@
 >
 <parameter name="context" type="android.content.Context">
 </parameter>
+<parameter name="mode" type="int">
+</parameter>
+</constructor>
+<constructor name="Spinner"
+ type="android.widget.Spinner"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
 <parameter name="attrs" type="android.util.AttributeSet">
 </parameter>
 </constructor>
@@ -232328,6 +233185,22 @@
 <parameter name="defStyle" type="int">
 </parameter>
 </constructor>
+<constructor name="Spinner"
+ type="android.widget.Spinner"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyle" type="int">
+</parameter>
+<parameter name="mode" type="int">
+</parameter>
+</constructor>
 <method name="getPrompt"
  return="java.lang.CharSequence"
  abstract="false"
@@ -237282,7 +238155,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
@@ -264495,7 +265368,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="lng" type="long">
+<parameter name="l" type="long">
 </parameter>
 </method>
 <method name="append"
@@ -264573,7 +265446,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="ch" type="char[]">
+<parameter name="chars" type="char[]">
 </parameter>
 </method>
 <method name="append"
@@ -277119,21 +277992,6 @@
  visibility="protected"
 >
 </method>
-<method name="getOption"
- return="java.lang.Object"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="optID" type="int">
-</parameter>
-<exception name="SocketException" type="java.net.SocketException">
-</exception>
-</method>
 <method name="getOutputStream"
  return="java.io.OutputStream"
  abstract="true"
@@ -277188,23 +278046,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="setOption"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="optID" type="int">
-</parameter>
-<parameter name="val" type="java.lang.Object">
-</parameter>
-<exception name="SocketException" type="java.net.SocketException">
-</exception>
-</method>
 <method name="setPerformancePreferences"
  return="void"
  abstract="false"
@@ -277583,19 +278424,6 @@
 <parameter name="p" type="java.security.Permission">
 </parameter>
 </method>
-<method name="isValidIP6Address"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="ipAddress" type="java.lang.String">
-</parameter>
-</method>
 </class>
 <class name="SocketTimeoutException"
  extends="java.io.InterruptedIOException"
@@ -291330,9 +292158,77 @@
  visibility="public"
 >
 </constructor>
+<method name="getInstance"
+ return="java.security.Policy"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="params" type="java.security.Policy.Parameters">
+</parameter>
+<exception name="NoSuchAlgorithmException" type="java.security.NoSuchAlgorithmException">
+</exception>
+</method>
+<method name="getInstance"
+ return="java.security.Policy"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="params" type="java.security.Policy.Parameters">
+</parameter>
+<parameter name="provider" type="java.lang.String">
+</parameter>
+<exception name="NoSuchAlgorithmException" type="java.security.NoSuchAlgorithmException">
+</exception>
+<exception name="NoSuchProviderException" type="java.security.NoSuchProviderException">
+</exception>
+</method>
+<method name="getInstance"
+ return="java.security.Policy"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="params" type="java.security.Policy.Parameters">
+</parameter>
+<parameter name="provider" type="java.security.Provider">
+</parameter>
+<exception name="NoSuchAlgorithmException" type="java.security.NoSuchAlgorithmException">
+</exception>
+</method>
+<method name="getParameters"
+ return="java.security.Policy.Parameters"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getPermissions"
  return="java.security.PermissionCollection"
- abstract="true"
+ abstract="false"
  native="false"
  synchronized="false"
  static="false"
@@ -291367,6 +292263,28 @@
  visibility="public"
 >
 </method>
+<method name="getProvider"
+ return="java.security.Provider"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="implies"
  return="boolean"
  abstract="false"
@@ -291384,7 +292302,7 @@
 </method>
 <method name="refresh"
  return="void"
- abstract="true"
+ abstract="false"
  native="false"
  synchronized="false"
  static="false"
@@ -291406,6 +292324,93 @@
 <parameter name="policy" type="java.security.Policy">
 </parameter>
 </method>
+<field name="UNSUPPORTED_EMPTY_COLLECTION"
+ type="java.security.PermissionCollection"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="Policy.Parameters"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</interface>
+<class name="PolicySpi"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PolicySpi"
+ type="java.security.PolicySpi"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="engineGetPermissions"
+ return="java.security.PermissionCollection"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="codesource" type="java.security.CodeSource">
+</parameter>
+</method>
+<method name="engineGetPermissions"
+ return="java.security.PermissionCollection"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="domain" type="java.security.ProtectionDomain">
+</parameter>
+</method>
+<method name="engineImplies"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="domain" type="java.security.ProtectionDomain">
+</parameter>
+<parameter name="permission" type="java.security.Permission">
+</parameter>
+</method>
+<method name="engineRefresh"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
 </class>
 <interface name="Principal"
  abstract="true"
@@ -319144,9 +320149,9 @@
 >
 <parameter name="number" type="java.lang.Object">
 </parameter>
-<parameter name="toAppendTo" type="java.lang.StringBuffer">
+<parameter name="buffer" type="java.lang.StringBuffer">
 </parameter>
-<parameter name="pos" type="java.text.FieldPosition">
+<parameter name="position" type="java.text.FieldPosition">
 </parameter>
 </method>
 <method name="getDecimalFormatSymbols"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 4265cc5..f901bfb 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -28,7 +28,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.AndroidException;
 import android.view.IWindowManager;
@@ -142,11 +141,31 @@
                 String value = nextArgRequired();
                 intent.putExtra(key, Integer.valueOf(value));
                 hasIntentInfo = true;
+            } else if (opt.equals("--eia")) {
+                String key = nextArgRequired();
+                String value = nextArgRequired();
+                String[] strings = value.split(",");
+                int[] list = new int[strings.length];
+                for (int i = 0; i < strings.length; i++) {
+                    list[i] = Integer.valueOf(strings[i]);
+                }
+                intent.putExtra(key, list);
+                hasIntentInfo = true;
             } else if (opt.equals("--el")) {
                 String key = nextArgRequired();
                 String value = nextArgRequired();
                 intent.putExtra(key, Long.valueOf(value));
                 hasIntentInfo = true;
+            } else if (opt.equals("--ela")) {
+                String key = nextArgRequired();
+                String value = nextArgRequired();
+                String[] strings = value.split(",");
+                long[] list = new long[strings.length];
+                for (int i = 0; i < strings.length; i++) {
+                    list[i] = Long.valueOf(strings[i]);
+                }
+                intent.putExtra(key, list);
+                hasIntentInfo = true;
             } else if (opt.equals("--ez")) {
                 String key = nextArgRequired();
                 String value = nextArgRequired();
@@ -620,6 +639,11 @@
                 "        -p <FILE>: write profiling data to <FILE>\n" +
                 "        -w: wait for instrumentation to finish before returning\n" +
                 "\n" +
+                "    run a test package against an application: am instrument [flags] <TEST_PACKAGE>/<RUNNER_CLASS>\n" +
+                "        -e <testrunner_flag> <testrunner_value> [,<testrunner_value>]\n" +
+                "        -w wait for the test to finish (required)\n" +
+                "        -r use with -e perf true to generate raw output for performance measurements\n" +
+                "\n" +
                 "    start profiling: am profile <PROCESS> start <FILE>\n" +
                 "    stop profiling: am profile <PROCESS> stop\n" +
                 "    dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" +
@@ -633,6 +657,8 @@
                 "        [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
                 "        [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
                 "        [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
+                "        [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
+                "        [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
                 "        [-n <COMPONENT>] [-f <FLAGS>]\n" +
                 "        [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
                 "        [--debug-log-resolution]\n" +
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 8f3642c..a09666e 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -115,6 +115,8 @@
             "su", "root", "wpa_cli", "list_networks", NULL);
 
 #ifdef FWDUMP_bcm4329
+    run_command("DUMP WIFI STATUS", 20,
+            "su", "root", "dhdutil", "-i", "eth0", "dump", NULL);
     run_command("DUMP WIFI FIRMWARE LOG", 60,
             "su", "root", "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL);
 #endif
@@ -239,8 +241,18 @@
 
     /* switch to non-root user and group */
     gid_t groups[] = { AID_LOG, AID_SDCARD_RW, AID_MOUNT };
-    setgroups(sizeof(groups)/sizeof(groups[0]), groups);
-    setuid(AID_SHELL);
+    if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
+        LOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
+        return -1;
+    }
+    if (setgid(AID_SHELL) != 0) {
+        LOGE("Unable to setgid, aborting: %s\n", strerror(errno));
+        return -1;
+    }
+    if (setuid(AID_SHELL) != 0) {
+        LOGE("Unable to setuid, aborting: %s\n", strerror(errno));
+        return -1;
+    }
 
     char path[PATH_MAX], tmp_path[PATH_MAX];
     pid_t gzip_pid = -1;
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 2e87394..f6f80d1 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -136,7 +136,7 @@
 /* TODO(oam): depending on use case (ecryptfs or dmcrypt)
  * change implementation
  */
-static int disk_free()
+static int64_t disk_free()
 {
     struct statfs sfs;
     if (statfs(PKG_DIR_PREFIX, &sfs) == 0) {
@@ -154,18 +154,18 @@
  * also require that apps constantly modify file metadata even
  * when just reading from the cache, which is pretty awful.
  */
-int free_cache(int free_size)
+int free_cache(int64_t free_size)
 {
     const char *name;
     int dfd, subfd;
     DIR *d;
     struct dirent *de;
-    int avail;
+    int64_t avail;
 
     avail = disk_free();
     if (avail < 0) return -1;
 
-    LOGI("free_cache(%d) avail %d\n", free_size, avail);
+    LOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail);
     if (avail >= free_size) return 0;
 
     /* First try encrypted dir */
@@ -327,10 +327,10 @@
     return 0;
 }
 
-static int stat_size(struct stat *s)
+static int64_t stat_size(struct stat *s)
 {
-    int blksize = s->st_blksize;
-    int size = s->st_size;
+    int64_t blksize = s->st_blksize;
+    int64_t size = s->st_size;
 
     if (blksize) {
             /* round up to filesystem block size */
@@ -340,9 +340,9 @@
     return size;
 }
 
-static int calculate_dir_size(int dfd)
+static int64_t calculate_dir_size(int dfd)
 {
-    int size = 0;
+    int64_t size = 0;
     struct stat s;
     DIR *d;
     struct dirent *de;
@@ -378,7 +378,7 @@
 
 int get_size(const char *pkgname, const char *apkpath,
              const char *fwdlock_apkpath,
-             int *_codesize, int *_datasize, int *_cachesize, int encrypted_fs_flag)
+             int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, int encrypted_fs_flag)
 {
     DIR *d;
     int dfd;
@@ -386,9 +386,9 @@
     struct stat s;
     char path[PKG_PATH_MAX];
 
-    int codesize = 0;
-    int datasize = 0;
-    int cachesize = 0;
+    int64_t codesize = 0;
+    int64_t datasize = 0;
+    int64_t cachesize = 0;
 
         /* count the source apk as code -- but only if it's not
          * on the /system partition and its not on the sdcard.
@@ -445,7 +445,7 @@
             }
             subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
             if (subfd >= 0) {
-                int size = calculate_dir_size(subfd);
+                int64_t size = calculate_dir_size(subfd);
                 if (!strcmp(name,"lib")) {
                     codesize += size;
                 } else if(!strcmp(name,"cache")) {
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 882c493..c991845 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -60,7 +60,7 @@
 
 static int do_free_cache(char **arg, char reply[REPLY_MAX]) /* TODO int:free_size */
 {
-    return free_cache(atoi(arg[0])); /* free_size */
+    return free_cache((int64_t)atoll(arg[0])); /* free_size */
 }
 
 static int do_rm_cache(char **arg, char reply[REPLY_MAX])
@@ -75,15 +75,19 @@
 
 static int do_get_size(char **arg, char reply[REPLY_MAX])
 {
-    int codesize = 0;
-    int datasize = 0;
-    int cachesize = 0;
+    int64_t codesize = 0;
+    int64_t datasize = 0;
+    int64_t cachesize = 0;
     int res = 0;
 
         /* pkgdir, apkpath */
     res = get_size(arg[0], arg[1], arg[2], &codesize, &datasize, &cachesize, atoi(arg[3]));
 
-    sprintf(reply,"%d %d %d", codesize, datasize, cachesize);
+    /*
+     * Each int64_t can take up 22 characters printed out. Make sure it
+     * doesn't go over REPLY_MAX in the future.
+     */
+    snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64, codesize, datasize, cachesize);
     return res;
 }
 
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 8e4adb1..479e4b2 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -19,6 +19,8 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
 #include <sys/stat.h>
 #include <dirent.h>
 #include <unistd.h>
@@ -105,7 +107,7 @@
 int rm_dex(const char *path);
 int protect(char *pkgname, gid_t gid);
 int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpath,
-             int *codesize, int *datasize, int *cachesize, int encrypted_fs_flag);
-int free_cache(int free_size);
+             int64_t *codesize, int64_t *datasize, int64_t *cachesize, int encrypted_fs_flag);
+int free_cache(int64_t free_size);
 int dexopt(const char *apk_path, uid_t uid, int is_public);
 int movefiles();
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 9b8b0ac..040421a 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -233,7 +233,7 @@
             for (int i=0; i<rawList.length; i++) {
                 list.add(rawList[i]);
             }
-                    
+
 
             // Sort by name
             Collections.sort(list, new Comparator<FeatureInfo>() {
@@ -909,6 +909,9 @@
         System.err.println("The list instrumentation command prints all instrumentations,");
         System.err.println("or only those that target a specified package.  Options:");
         System.err.println("  -f: see their associated file.");
+        System.err.println("(Use this command to list all test packages, or use <TARGET-PACKAGE> ");
+        System.err.println(" to list the test packages for a particular application. The -f ");
+        System.err.println(" option lists the .apk file for the test package.)");
         System.err.println("");
         System.err.println("The list features command prints all features of the system.");
         System.err.println("");
diff --git a/core/java/android/animation/Animatable.java b/core/java/android/animation/Animatable.java
deleted file mode 100644
index d6cf7c0..0000000
--- a/core/java/android/animation/Animatable.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * 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.animation;
-
-import java.util.ArrayList;
-
-/**
- * This is the superclass for classes which provide basic support for animations which can be
- * started, ended, and have <code>AnimatableListeners</code> added to them.
- */
-public abstract class Animatable implements Cloneable {
-
-
-    /**
-     * The set of listeners to be sent events through the life of an animation.
-     */
-    ArrayList<AnimatableListener> mListeners = null;
-
-    /**
-     * Starts this animation. If the animation has a nonzero startDelay, the animation will start
-     * running after that delay elapses. Note that the animation does not start synchronously with
-     * this call, because all animation events are posted to a central timing loop so that animation
-     * times are all synchronized on a single timing pulse on the UI thread. So the animation will
-     * start the next time that event handler processes events.
-     */
-    public void start() {
-    }
-
-    /**
-     * Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
-     * stop in its tracks, sending an {@link AnimatableListener#onAnimationCancel(Animatable)} to
-     * its listeners, followed by an {@link AnimatableListener#onAnimationEnd(Animatable)} message.
-     */
-    public void cancel() {
-    }
-
-    /**
-     * Ends the animation. This causes the animation to assign the end value of the property being
-     * animated, then calling the {@link AnimatableListener#onAnimationEnd(Animatable)} method on
-     * its listeners.
-     */
-    public void end() {
-    }
-
-
-    /**
-     * Returns whether this Animatable is currently running (having been started and not yet ended).
-     * @return Whether the Animatable is running.
-     */
-    public abstract boolean isRunning();
-
-    /**
-     * Adds a listener to the set of listeners that are sent events through the life of an
-     * animation, such as start, repeat, and end.
-     *
-     * @param listener the listener to be added to the current set of listeners for this animation.
-     */
-    public void addListener(AnimatableListener listener) {
-        if (mListeners == null) {
-            mListeners = new ArrayList<AnimatableListener>();
-        }
-        mListeners.add(listener);
-    }
-
-    /**
-     * Removes a listener from the set listening to this animation.
-     *
-     * @param listener the listener to be removed from the current set of listeners for this
-     *                 animation.
-     */
-    public void removeListener(AnimatableListener listener) {
-        if (mListeners == null) {
-            return;
-        }
-        mListeners.remove(listener);
-        if (mListeners.size() == 0) {
-            mListeners = null;
-        }
-    }
-
-    /**
-     * Gets the set of {@link AnimatableListener} objects that are currently
-     * listening for events on this <code>Animatable</code> object.
-     *
-     * @return ArrayList<AnimatableListener> The set of listeners.
-     */
-    public ArrayList<AnimatableListener> getListeners() {
-        return mListeners;
-    }
-
-    /**
-     * Removes all listeners from this object. This is equivalent to calling
-     * <code>getListeners()</code> followed by calling <code>clear()</code> on the
-     * returned list of listeners.
-     */
-    public void removeAllListeners() {
-        if (mListeners != null) {
-            mListeners.clear();
-            mListeners = null;
-        }
-    }
-
-    @Override
-    public Animatable clone() throws CloneNotSupportedException {
-        final Animatable anim = (Animatable) super.clone();
-        if (mListeners != null) {
-            ArrayList<AnimatableListener> oldListeners = mListeners;
-            anim.mListeners = new ArrayList<AnimatableListener>();
-            int numListeners = oldListeners.size();
-            for (int i = 0; i < numListeners; ++i) {
-                anim.mListeners.add(oldListeners.get(i));
-            }
-        }
-        return anim;
-    }
-
-    /**
-     * <p>An animation listener receives notifications from an animation.
-     * Notifications indicate animation related events, such as the end or the
-     * repetition of the animation.</p>
-     */
-    public static interface AnimatableListener {
-        /**
-         * <p>Notifies the start of the animation.</p>
-         *
-         * @param animation The started animation.
-         */
-        void onAnimationStart(Animatable animation);
-
-        /**
-         * <p>Notifies the end of the animation. This callback is not invoked
-         * for animations with repeat count set to INFINITE.</p>
-         *
-         * @param animation The animation which reached its end.
-         */
-        void onAnimationEnd(Animatable animation);
-
-        /**
-         * <p>Notifies the cancellation of the animation. This callback is not invoked
-         * for animations with repeat count set to INFINITE.</p>
-         *
-         * @param animation The animation which was canceled.
-         */
-        void onAnimationCancel(Animatable animation);
-
-        /**
-         * <p>Notifies the repetition of the animation.</p>
-         *
-         * @param animation The animation which was repeated.
-         */
-        void onAnimationRepeat(Animatable animation);
-    }
-}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
old mode 100755
new mode 100644
index 8b74658..2ada6d6
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -16,441 +16,46 @@
 
 package android.animation;
 
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 
 /**
- * This class provides a simple timing engine for running animations
- * which calculate animated values and set them on target objects.
- *
- * <p>There is a single timing pulse that all animations use. It runs in a
- * custom handler to ensure that property changes happen on the UI thread.</p>
- *
- * <p>By default, Animator uses non-linear time interpolation, via the
- * {@link AccelerateDecelerateInterpolator} class, which accelerates into and decelerates
- * out of an animation. This behavior can be changed by calling
- * {@link Animator#setInterpolator(Interpolator)}.</p>
+ * This is the superclass for classes which provide basic support for animations which can be
+ * started, ended, and have <code>AnimatorListeners</code> added to them.
  */
-public class Animator<T> extends Animatable {
+public abstract class Animator implements Cloneable {
 
-    /**
-     * Internal constants
-     */
-
-    /*
-     * The default amount of time in ms between animation frames
-     */
-    private static final long DEFAULT_FRAME_DELAY = 30;
-
-    /**
-     * Messages sent to timing handler: START is sent when an animation first begins, FRAME is sent
-     * by the handler to itself to process the next animation frame
-     */
-    private static final int ANIMATION_START = 0;
-    private static final int ANIMATION_FRAME = 1;
-
-    /**
-     * Values used with internal variable mPlayingState to indicate the current state of an
-     * animation.
-     */
-    private static final int STOPPED    = 0; // Not yet playing
-    private static final int RUNNING    = 1; // Playing normally
-    private static final int CANCELED   = 2; // cancel() called - need to end it
-    private static final int ENDED      = 3; // end() called - need to end it
-    private static final int SEEKED     = 4; // Seeked to some time value
-
-    /**
-     * Internal variables
-     * NOTE: This object implements the clone() method, making a deep copy of any referenced
-     * objects. As other non-trivial fields are added to this class, make sure to add logic
-     * to clone() to make deep copies of them.
-     */
-
-    // The first time that the animation's animateFrame() method is called. This time is used to
-    // determine elapsed time (and therefore the elapsed fraction) in subsequent calls
-    // to animateFrame()
-    private long mStartTime;
-
-    /**
-     * Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
-     * to a value.
-     */
-    private long mSeekTime = -1;
-
-    // The static sAnimationHandler processes the internal timing loop on which all animations
-    // are based
-    private static AnimationHandler sAnimationHandler;
-
-    // The static list of all active animations
-    private static final ArrayList<Animator> sAnimations = new ArrayList<Animator>();
-
-    // The set of animations to be started on the next animation frame
-    private static final ArrayList<Animator> sPendingAnimations = new ArrayList<Animator>();
-
-    // The time interpolator to be used if none is set on the animation
-    private static final Interpolator sDefaultInterpolator = new AccelerateDecelerateInterpolator();
-
-    // type evaluators for the three primitive types handled by this implementation
-    private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
-    private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
-    private static final TypeEvaluator sDoubleEvaluator = new DoubleEvaluator();
-
-    /**
-     * Used to indicate whether the animation is currently playing in reverse. This causes the
-     * elapsed fraction to be inverted to calculate the appropriate values.
-     */
-    private boolean mPlayingBackwards = false;
-
-    /**
-     * This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the
-     * repeatCount (if repeatCount!=INFINITE), the animation ends
-     */
-    private int mCurrentIteration = 0;
-
-    /**
-     * Tracks whether a startDelay'd animation has begun playing through the startDelay.
-     */
-    private boolean mStartedDelay = false;
-
-    /**
-     * Tracks the time at which the animation began playing through its startDelay. This is
-     * different from the mStartTime variable, which is used to track when the animation became
-     * active (which is when the startDelay expired and the animation was added to the active
-     * animations list).
-     */
-    private long mDelayStartTime;
-
-    /**
-     * Flag that represents the current state of the animation. Used to figure out when to start
-     * an animation (if state == STOPPED). Also used to end an animation that
-     * has been cancel()'d or end()'d since the last animation frame. Possible values are
-     * STOPPED, RUNNING, ENDED, CANCELED.
-     */
-    private int mPlayingState = STOPPED;
-
-    /**
-     * Internal collections used to avoid set collisions as animations start and end while being
-     * processed.
-     */
-    private static final ArrayList<Animator> sEndingAnims = new ArrayList<Animator>();
-    private static final ArrayList<Animator> sDelayedAnims = new ArrayList<Animator>();
-    private static final ArrayList<Animator> sReadyAnims = new ArrayList<Animator>();
-
-    /**
-     * Flag that denotes whether the animation is set up and ready to go. Used by seek() to
-     * set up animation that has not yet been started.
-     */
-    private boolean mInitialized = false;
-
-    //
-    // Backing variables
-    //
-
-    // How long the animation should last in ms
-    private long mDuration;
-
-    // The amount of time in ms to delay starting the animation after start() is called
-    private long mStartDelay = 0;
-
-    // The number of milliseconds between animation frames
-    private static long sFrameDelay = DEFAULT_FRAME_DELAY;
-
-    // The number of times the animation will repeat. The default is 0, which means the animation
-    // will play only once
-    private int mRepeatCount = 0;
-
-    /**
-     * The type of repetition that will occur when repeatMode is nonzero. RESTART means the
-     * animation will start from the beginning on every new cycle. REVERSE means the animation
-     * will reverse directions on each iteration.
-     */
-    private int mRepeatMode = RESTART;
-
-    /**
-     * The time interpolator to be used. The elapsed fraction of the animation will be passed
-     * through this interpolator to calculate the interpolated fraction, which is then used to
-     * calculate the animated values.
-     */
-    private Interpolator mInterpolator = sDefaultInterpolator;
 
     /**
      * The set of listeners to be sent events through the life of an animation.
      */
-    private ArrayList<AnimatorUpdateListener> mUpdateListeners = null;
+    ArrayList<AnimatorListener> mListeners = null;
 
     /**
-     * The property/value sets being animated.
+     * Starts this animation. If the animation has a nonzero startDelay, the animation will start
+     * running after that delay elapses. Note that the animation does not start synchronously with
+     * this call, because all animation events are posted to a central timing loop so that animation
+     * times are all synchronized on a single timing pulse on the UI thread. So the animation will
+     * start the next time that event handler processes events.
      */
-    PropertyValuesHolder[] mValues;
-
-    /**
-     * A hashmap of the PropertyValuesHolder objects. This map is used to lookup animated values
-     * by property name during calls to getAnimatedValue(String).
-     */
-    HashMap<String, PropertyValuesHolder> mValuesMap;
-
-    /**
-     * Public constants
-     */
-
-    /**
-     * When the animation reaches the end and <code>repeatCount</code> is INFINITE
-     * or a positive value, the animation restarts from the beginning.
-     */
-    public static final int RESTART = 1;
-    /**
-     * When the animation reaches the end and <code>repeatCount</code> is INFINITE
-     * or a positive value, the animation reverses direction on every iteration.
-     */
-    public static final int REVERSE = 2;
-    /**
-     * This value used used with the {@link #setRepeatCount(int)} property to repeat
-     * the animation indefinitely.
-     */
-    public static final int INFINITE = -1;
-
-    /**
-     * Creates a new Animator object. This default constructor is primarily for
-     * use internally; the other constructors which take parameters are more generally
-     * useful.
-     */
-    public Animator() {
+    public void start() {
     }
 
     /**
-     * Constructs an Animator object with the specified duration and set of
-     * values. If the values are a set of PropertyValuesHolder objects, then these objects
-     * define the potentially multiple properties being animated and the values the properties are
-     * animated between. Otherwise, the values define a single set of values animated between.
-     *
-     * @param duration The length of the animation, in milliseconds.
-     * @param values The set of values to animate between. If these values are not
-     * PropertyValuesHolder objects, then there should be more than one value, since the values
-     * determine the interval to animate between.
+     * Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
+     * stop in its tracks, sending an {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
+     * its listeners, followed by an {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
      */
-    public Animator(long duration, T...values) {
-        mDuration = duration;
-        if (values.length > 0) {
-            setValues(values);
-        }
-    }
-
-    public void setValues(PropertyValuesHolder... values) {
-        int numValues = values.length;
-        mValues = values;
-        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
-        for (int i = 0; i < numValues; ++i) {
-            PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
-            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
-        }
+    public void cancel() {
     }
 
     /**
-     * Sets the values to animate between for this animation. If <code>values</code> is
-     * a set of PropertyValuesHolder objects, these objects will become the set of properties
-     * animated and the values that those properties are animated between. Otherwise, this method
-     * will set only one set of values for the Animator. Also, if the values are not
-     * PropertyValuesHolder objects and if there are already multiple sets of
-     * values defined for this Animator via
-     * more than one PropertyValuesHolder objects, this method will set the values for
-     * the first of those objects.
-     *
-     * @param values The set of values to animate between.
+     * Ends the animation. This causes the animation to assign the end value of the property being
+     * animated, then calling the {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
+     * its listeners.
      */
-    public void setValues(T... values) {
-        if (mValues == null || mValues.length == 0) {
-            setValues(new PropertyValuesHolder[]{
-                    new PropertyValuesHolder("", (Object[])values)});
-        } else {
-            PropertyValuesHolder valuesHolder = mValues[0];
-            valuesHolder.setValues(values);
-        }
-    }
-
-    /**
-     * This function is called immediately before processing the first animation
-     * frame of an animation. If there is a nonzero <code>startDelay</code>, the
-     * function is called after that delay ends.
-     * It takes care of the final initialization steps for the
-     * animation.
-     *
-     *  <p>Overrides of this method should call the superclass method to ensure
-     *  that internal mechanisms for the animation are set up correctly.</p>
-     */
-    void initAnimation() {
-        int numValues = mValues.length;
-        for (int i = 0; i < numValues; ++i) {
-            mValues[i].init();
-        }
-        mCurrentIteration = 0;
-        mInitialized = true;
-    }
-
-
-    /**
-     * Sets the length of the animation.
-     *
-     * @param duration The length of the animation, in milliseconds.
-     */
-    public void setDuration(long duration) {
-        mDuration = duration;
-    }
-
-    /**
-     * Gets the length of the animation.
-     *
-     * @return The length of the animation, in milliseconds.
-     */
-    public long getDuration() {
-        return mDuration;
-    }
-
-    /**
-     * Sets the position of the animation to the specified point in time. This time should
-     * be between 0 and the total duration of the animation, including any repetition. If
-     * the animation has not yet been started, then it will not advance forward after it is
-     * set to this time; it will simply set the time to this value and perform any appropriate
-     * actions based on that time. If the animation is already running, then setCurrentPlayTime()
-     * will set the current playing time to this value and continue playing from that point.
-     *
-     * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
-     */
-    public void setCurrentPlayTime(long playTime) {
-        if (!mInitialized) {
-            initAnimation();
-        }
-        long currentTime = AnimationUtils.currentAnimationTimeMillis();
-        if (mPlayingState != RUNNING) {
-            mSeekTime = playTime;
-            mPlayingState = SEEKED;
-        }
-        mStartTime = currentTime - playTime;
-        animationFrame(currentTime);
-    }
-
-    /**
-     * Gets the current position of the animation in time, which is equal to the current
-     * time minus the time that the animation started. An animation that is not yet started will
-     * return a value of zero.
-     *
-     * @return The current position in time of the animation.
-     */
-    public long getCurrentPlayTime() {
-        if (!mInitialized || mPlayingState == STOPPED) {
-            return 0;
-        }
-        return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
-    }
-
-    /**
-     * This custom, static handler handles the timing pulse that is shared by
-     * all active animations. This approach ensures that the setting of animation
-     * values will happen on the UI thread and that all animations will share
-     * the same times for calculating their values, which makes synchronizing
-     * animations possible.
-     *
-     */
-    private static class AnimationHandler extends Handler {
-        /**
-         * There are only two messages that we care about: ANIMATION_START and
-         * ANIMATION_FRAME. The START message is sent when an animation's start()
-         * method is called. It cannot start synchronously when start() is called
-         * because the call may be on the wrong thread, and it would also not be
-         * synchronized with other animations because it would not start on a common
-         * timing pulse. So each animation sends a START message to the handler, which
-         * causes the handler to place the animation on the active animations queue and
-         * start processing frames for that animation.
-         * The FRAME message is the one that is sent over and over while there are any
-         * active animations to process.
-         */
-        @Override
-        public void handleMessage(Message msg) {
-            boolean callAgain = true;
-            switch (msg.what) {
-                // TODO: should we avoid sending frame message when starting if we
-                // were already running?
-                case ANIMATION_START:
-                    if (sAnimations.size() > 0 || sDelayedAnims.size() > 0) {
-                        callAgain = false;
-                    }
-                    // pendingAnims holds any animations that have requested to be started
-                    // We're going to clear sPendingAnimations, but starting animation may
-                    // cause more to be added to the pending list (for example, if one animation
-                    // starting triggers another starting). So we loop until sPendingAnimations
-                    // is empty.
-                    while (sPendingAnimations.size() > 0) {
-                        ArrayList<Animator> pendingCopy =
-                                (ArrayList<Animator>) sPendingAnimations.clone();
-                        sPendingAnimations.clear();
-                        int count = pendingCopy.size();
-                        for (int i = 0; i < count; ++i) {
-                            Animator anim = pendingCopy.get(i);
-                            // If the animation has a startDelay, place it on the delayed list
-                            if (anim.mStartDelay == 0 || anim.mPlayingState == ENDED ||
-                                    anim.mPlayingState == CANCELED) {
-                                anim.startAnimation();
-                            } else {
-                                sDelayedAnims.add(anim);
-                            }
-                        }
-                    }
-                    // fall through to process first frame of new animations
-                case ANIMATION_FRAME:
-                    // currentTime holds the common time for all animations processed
-                    // during this frame
-                    long currentTime = AnimationUtils.currentAnimationTimeMillis();
-
-                    // First, process animations currently sitting on the delayed queue, adding
-                    // them to the active animations if they are ready
-                    int numDelayedAnims = sDelayedAnims.size();
-                    for (int i = 0; i < numDelayedAnims; ++i) {
-                        Animator anim = sDelayedAnims.get(i);
-                        if (anim.delayedAnimationFrame(currentTime)) {
-                            sReadyAnims.add(anim);
-                        }
-                    }
-                    int numReadyAnims = sReadyAnims.size();
-                    if (numReadyAnims > 0) {
-                        for (int i = 0; i < numReadyAnims; ++i) {
-                            Animator anim = sReadyAnims.get(i);
-                            anim.startAnimation();
-                            sDelayedAnims.remove(anim);
-                        }
-                        sReadyAnims.clear();
-                    }
-
-                    // Now process all active animations. The return value from animationFrame()
-                    // tells the handler whether it should now be ended
-                    int numAnims = sAnimations.size();
-                    for (int i = 0; i < numAnims; ++i) {
-                        Animator anim = sAnimations.get(i);
-                        if (anim.animationFrame(currentTime)) {
-                            sEndingAnims.add(anim);
-                        }
-                    }
-                    if (sEndingAnims.size() > 0) {
-                        for (int i = 0; i < sEndingAnims.size(); ++i) {
-                            sEndingAnims.get(i).endAnimation();
-                        }
-                        sEndingAnims.clear();
-                    }
-
-                    // If there are still active or delayed animations, call the handler again
-                    // after the frameDelay
-                    if (callAgain && (!sAnimations.isEmpty() || !sDelayedAnims.isEmpty())) {
-                        sendEmptyMessageDelayed(ANIMATION_FRAME, sFrameDelay);
-                    }
-                    break;
-            }
-        }
+    public void end() {
     }
 
     /**
@@ -459,9 +64,7 @@
      *
      * @return the number of milliseconds to delay running the animation
      */
-    public long getStartDelay() {
-        return mStartDelay;
-    }
+    public abstract long getStartDelay();
 
     /**
      * The amount of time, in milliseconds, to delay starting the animation after
@@ -469,147 +72,22 @@
 
      * @param startDelay The amount of the delay, in milliseconds
      */
-    public void setStartDelay(long startDelay) {
-        this.mStartDelay = startDelay;
-    }
+    public abstract void setStartDelay(long startDelay);
+
 
     /**
-     * The amount of time, in milliseconds, between each frame of the animation. This is a
-     * requested time that the animation will attempt to honor, but the actual delay between
-     * frames may be different, depending on system load and capabilities. This is a static
-     * function because the same delay will be applied to all animations, since they are all
-     * run off of a single timing loop.
+     * Sets the length of the animation.
      *
-     * @return the requested time between frames, in milliseconds
+     * @param duration The length of the animation, in milliseconds.
      */
-    public static long getFrameDelay() {
-        return sFrameDelay;
-    }
+    public abstract void setDuration(long duration);
 
     /**
-     * The amount of time, in milliseconds, between each frame of the animation. This is a
-     * requested time that the animation will attempt to honor, but the actual delay between
-     * frames may be different, depending on system load and capabilities. This is a static
-     * function because the same delay will be applied to all animations, since they are all
-     * run off of a single timing loop.
+     * Gets the length of the animation.
      *
-     * @param frameDelay the requested time between frames, in milliseconds
+     * @return The length of the animation, in milliseconds.
      */
-    public static void setFrameDelay(long frameDelay) {
-        sFrameDelay = frameDelay;
-    }
-
-    /**
-     * The most recent value calculated by this <code>Animator</code> when there is just one
-     * property being animated. This value is only sensible while the animation is running. The main
-     * purpose for this read-only property is to retrieve the value from the <code>Animator</code>
-     * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(Animator)}, which
-     * is called during each animation frame, immediately after the value is calculated.
-     *
-     * @return animatedValue The value most recently calculated by this <code>Animator</code> for
-     * the single property being animated. If there are several properties being animated
-     * (specified by several PropertyValuesHolder objects in the constructor), this function
-     * returns the animated value for the first of those objects.
-     */
-    public Object getAnimatedValue() {
-        if (mValues != null && mValues.length > 0) {
-            return mValues[0].getAnimatedValue();
-        }
-        // Shouldn't get here; should always have values unless Animator was set up wrong
-        return null;
-    }
-
-    /**
-     * The most recent value calculated by this <code>Animator</code> for <code>propertyName</code>.
-     * The main purpose for this read-only property is to retrieve the value from the
-     * <code>Animator</code> during a call to
-     * {@link AnimatorUpdateListener#onAnimationUpdate(Animator)}, which
-     * is called during each animation frame, immediately after the value is calculated.
-     *
-     * @return animatedValue The value most recently calculated for the named property
-     * by this <code>Animator</code>.
-     */
-    public Object getAnimatedValue(String propertyName) {
-        PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName);
-        if (valuesHolder != null) {
-            return valuesHolder.getAnimatedValue();
-        } else {
-            // At least avoid crashing if called with bogus propertyName
-            return null;
-        }
-    }
-
-    /**
-     * Sets how many times the animation should be repeated. If the repeat
-     * count is 0, the animation is never repeated. If the repeat count is
-     * greater than 0 or {@link #INFINITE}, the repeat mode will be taken
-     * into account. The repeat count is 0 by default.
-     *
-     * @param value the number of times the animation should be repeated
-     */
-    public void setRepeatCount(int value) {
-        mRepeatCount = value;
-    }
-    /**
-     * Defines how many times the animation should repeat. The default value
-     * is 0.
-     *
-     * @return the number of times the animation should repeat, or {@link #INFINITE}
-     */
-    public int getRepeatCount() {
-        return mRepeatCount;
-    }
-
-    /**
-     * Defines what this animation should do when it reaches the end. This
-     * setting is applied only when the repeat count is either greater than
-     * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
-     *
-     * @param value {@link #RESTART} or {@link #REVERSE}
-     */
-    public void setRepeatMode(int value) {
-        mRepeatMode = value;
-    }
-
-    /**
-     * Defines what this animation should do when it reaches the end.
-     *
-     * @return either one of {@link #REVERSE} or {@link #RESTART}
-     */
-    public int getRepeatMode() {
-        return mRepeatMode;
-    }
-
-    /**
-     * Adds a listener to the set of listeners that are sent update events through the life of
-     * an animation. This method is called on all listeners for every frame of the animation,
-     * after the values for the animation have been calculated.
-     *
-     * @param listener the listener to be added to the current set of listeners for this animation.
-     */
-    public void addUpdateListener(AnimatorUpdateListener listener) {
-        if (mUpdateListeners == null) {
-            mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
-        }
-        mUpdateListeners.add(listener);
-    }
-
-    /**
-     * Removes a listener from the set listening to frame updates for this animation.
-     *
-     * @param listener the listener to be removed from the current set of update listeners
-     * for this animation.
-     */
-    public void removeUpdateListener(AnimatorUpdateListener listener) {
-        if (mUpdateListeners == null) {
-            return;
-        }
-        mUpdateListeners.remove(listener);
-        if (mUpdateListeners.size() == 0) {
-            mUpdateListeners = null;
-        }
-    }
-
+    public abstract long getDuration();
 
     /**
      * The time interpolator used in calculating the elapsed fraction of this animation. The
@@ -619,330 +97,152 @@
      *
      * @param value the interpolator to be used by this animation
      */
-    public void setInterpolator(Interpolator value) {
-        if (value != null) {
-            mInterpolator = value;
+    public abstract void setInterpolator(Interpolator value);
+
+    /**
+     * Returns whether this Animator is currently running (having been started and not yet ended).
+     * @return Whether the Animator is running.
+     */
+    public abstract boolean isRunning();
+
+    /**
+     * Adds a listener to the set of listeners that are sent events through the life of an
+     * animation, such as start, repeat, and end.
+     *
+     * @param listener the listener to be added to the current set of listeners for this animation.
+     */
+    public void addListener(AnimatorListener listener) {
+        if (mListeners == null) {
+            mListeners = new ArrayList<AnimatorListener>();
+        }
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener from the set listening to this animation.
+     *
+     * @param listener the listener to be removed from the current set of listeners for this
+     *                 animation.
+     */
+    public void removeListener(AnimatorListener listener) {
+        if (mListeners == null) {
+            return;
+        }
+        mListeners.remove(listener);
+        if (mListeners.size() == 0) {
+            mListeners = null;
         }
     }
 
     /**
-     * Returns the timing interpolator that this Animator uses.
+     * Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
+     * listening for events on this <code>Animator</code> object.
      *
-     * @return The timing interpolator for this Animator.
+     * @return ArrayList<AnimatorListener> The set of listeners.
      */
-    public Interpolator getInterpolator() {
-        return mInterpolator;
+    public ArrayList<AnimatorListener> getListeners() {
+        return mListeners;
     }
 
     /**
-     * The type evaluator to be used when calculating the animated values of this animation.
-     * The system will automatically assign a float, int, or double evaluator based on the type
-     * of <code>startValue</code> and <code>endValue</code> in the constructor. But if these values
-     * are not one of these primitive types, or if different evaluation is desired (such as is
-     * necessary with int values that represent colors), a custom evaluator needs to be assigned.
-     * For example, when running an animation on color values, the {@link RGBEvaluator}
-     * should be used to get correct RGB color interpolation.
-     *
-     * <p>If this Animator has only one set of values being animated between, this evaluator
-     * will be used for that set. If there are several sets of values being animated, which is
-     * the case if PropertyValuesHOlder objects were set on the Animator, then the evaluator
-     * is assigned just to the first PropertyValuesHolder object.</p>
-     *
-     * @param value the evaluator to be used this animation
+     * Removes all listeners from this object. This is equivalent to calling
+     * <code>getListeners()</code> followed by calling <code>clear()</code> on the
+     * returned list of listeners.
      */
-    public void setEvaluator(TypeEvaluator value) {
-        if (value != null && mValues != null && mValues.length > 0) {
-            mValues[0].setEvaluator(value);
-        }
-    }
-
-    /**
-     * 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/
-     *
-     * @param playBackwards Whether the Animator should start playing in reverse.
-     */
-    private void start(boolean playBackwards) {
-        mPlayingBackwards = playBackwards;
-        if ((mStartDelay == 0) && (Thread.currentThread() == Looper.getMainLooper().getThread())) {
-            // This sets the initial value of the animation, prior to actually starting it running
-            setCurrentPlayTime(getCurrentPlayTime());
-        }
-        mPlayingState = STOPPED;
-        mStartedDelay = false;
-        sPendingAnimations.add(this);
-        if (sAnimationHandler == null) {
-            sAnimationHandler = new AnimationHandler();
-        }
-        // TODO: does this put too many messages on the queue if the handler
-        // is already running?
-        sAnimationHandler.sendEmptyMessage(ANIMATION_START);
-    }
-
-    @Override
-    public void start() {
-        start(false);
-    }
-
-    @Override
-    public void cancel() {
+    public void removeAllListeners() {
         if (mListeners != null) {
-            ArrayList<AnimatableListener> tmpListeners =
-                    (ArrayList<AnimatableListener>) mListeners.clone();
-            for (AnimatableListener listener : tmpListeners) {
-                listener.onAnimationCancel(this);
-            }
+            mListeners.clear();
+            mListeners = null;
         }
-        // Just set the CANCELED flag - this causes the animation to end the next time a frame
-        // is processed.
-        mPlayingState = CANCELED;
     }
 
     @Override
-    public void end() {
-        if (!sAnimations.contains(this) && !sPendingAnimations.contains(this)) {
-            // Special case if the animation has not yet started; get it ready for ending
-            mStartedDelay = false;
-            sPendingAnimations.add(this);
-            if (sAnimationHandler == null) {
-                sAnimationHandler = new AnimationHandler();
-            }
-            sAnimationHandler.sendEmptyMessage(ANIMATION_START);
-        }
-        // Just set the ENDED flag - this causes the animation to end the next time a frame
-        // is processed.
-        mPlayingState = ENDED;
-    }
-
-    @Override
-    public boolean isRunning() {
-        // ENDED or CANCELED indicate that it has been ended or canceled, but not processed yet
-        return (mPlayingState == RUNNING || mPlayingState == ENDED || mPlayingState == CANCELED);
-    }
-
-    /**
-     * Plays the Animator in reverse. If the animation is already running,
-     * it will stop itself and play backwards from the point reached when reverse was called.
-     * If the animation is not currently running, then it will start from the end and
-     * play backwards. This behavior is only set for the current animation; future playing
-     * of the animation will use the default behavior of playing forward.
-     */
-    public void reverse() {
-        mPlayingBackwards = !mPlayingBackwards;
-        if (mPlayingState == RUNNING) {
-            long currentTime = AnimationUtils.currentAnimationTimeMillis();
-            long currentPlayTime = currentTime - mStartTime;
-            long timeLeft = mDuration - currentPlayTime;
-            mStartTime = currentTime - timeLeft;
-        } else {
-            start(true);
-        }
-    }
-
-    /**
-     * Called internally to end an animation by removing it from the animations list. Must be
-     * called on the UI thread.
-     */
-    private void endAnimation() {
-        sAnimations.remove(this);
-        mPlayingState = STOPPED;
-        if (mListeners != null) {
-            ArrayList<AnimatableListener> tmpListeners =
-                    (ArrayList<AnimatableListener>) mListeners.clone();
-            for (AnimatableListener listener : tmpListeners) {
-                listener.onAnimationEnd(this);
-            }
-        }
-    }
-
-    /**
-     * Called internally to start an animation by adding it to the active animations list. Must be
-     * called on the UI thread.
-     */
-    private void startAnimation() {
-        initAnimation();
-        sAnimations.add(this);
-        if (mListeners != null) {
-            ArrayList<AnimatableListener> tmpListeners =
-                    (ArrayList<AnimatableListener>) mListeners.clone();
-            for (AnimatableListener listener : tmpListeners) {
-                listener.onAnimationStart(this);
-            }
-        }
-    }
-
-    /**
-     * Internal function called to process an animation frame on an animation that is currently
-     * sleeping through its <code>startDelay</code> phase. The return value indicates whether it
-     * should be woken up and put on the active animations queue.
-     *
-     * @param currentTime The current animation time, used to calculate whether the animation
-     * has exceeded its <code>startDelay</code> and should be started.
-     * @return True if the animation's <code>startDelay</code> has been exceeded and the animation
-     * should be added to the set of active animations.
-     */
-    private boolean delayedAnimationFrame(long currentTime) {
-        if (!mStartedDelay) {
-            mStartedDelay = true;
-            mDelayStartTime = currentTime;
-        } else {
-            long deltaTime = currentTime - mDelayStartTime;
-            if (deltaTime > mStartDelay) {
-                // startDelay ended - start the anim and record the
-                // mStartTime appropriately
-                mStartTime = currentTime - (deltaTime - mStartDelay);
-                mPlayingState = RUNNING;
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * This internal function processes a single animation frame for a given animation. The
-     * currentTime parameter is the timing pulse sent by the handler, used to calculate the
-     * elapsed duration, and therefore
-     * the elapsed fraction, of the animation. The return value indicates whether the animation
-     * should be ended (which happens when the elapsed time of the animation exceeds the
-     * animation's duration, including the repeatCount).
-     *
-     * @param currentTime The current time, as tracked by the static timing handler
-     * @return true if the animation's duration, including any repetitions due to
-     * <code>repeatCount</code> has been exceeded and the animation should be ended.
-     */
-    private boolean animationFrame(long currentTime) {
-        boolean done = false;
-
-        if (mPlayingState == STOPPED) {
-            mPlayingState = RUNNING;
-            if (mSeekTime < 0) {
-                mStartTime = currentTime;
-            } else {
-                mStartTime = currentTime - mSeekTime;
-                // Now that we're playing, reset the seek time
-                mSeekTime = -1;
-            }
-        }
-        switch (mPlayingState) {
-        case RUNNING:
-        case SEEKED:
-            float fraction = (float)(currentTime - mStartTime) / mDuration;
-            if (fraction >= 1f) {
-                if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
-                    // Time to repeat
-                    if (mListeners != null) {
-                        for (AnimatableListener listener : mListeners) {
-                            listener.onAnimationRepeat(this);
-                        }
-                    }
-                    ++mCurrentIteration;
-                    if (mRepeatMode == REVERSE) {
-                        mPlayingBackwards = mPlayingBackwards ? false : true;
-                    }
-                    // TODO: doesn't account for fraction going Wayyyyy over 1, like 2+
-                    fraction = fraction - 1f;
-                    mStartTime += mDuration;
-                } else {
-                    done = true;
-                    fraction = Math.min(fraction, 1.0f);
+    public Animator clone() {
+        try {
+            final Animator anim = (Animator) super.clone();
+            if (mListeners != null) {
+                ArrayList<AnimatorListener> oldListeners = mListeners;
+                anim.mListeners = new ArrayList<AnimatorListener>();
+                int numListeners = oldListeners.size();
+                for (int i = 0; i < numListeners; ++i) {
+                    anim.mListeners.add(oldListeners.get(i));
                 }
             }
-            if (mPlayingBackwards) {
-                fraction = 1f - fraction;
-            }
-            animateValue(fraction);
-            break;
-        case ENDED:
-            // The final value set on the target varies, depending on whether the animation
-            // was supposed to repeat an odd number of times
-            if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
-                animateValue(0f);
-            } else {
-                animateValue(1f);
-            }
-            // Fall through to set done flag
-        case CANCELED:
-            done = true;
-            mPlayingState = STOPPED;
-            break;
+            return anim;
+        } catch (CloneNotSupportedException e) {
+           throw new AssertionError();
         }
-
-        return done;
     }
 
     /**
-     * This method is called with the elapsed fraction of the animation during every
-     * animation frame. This function turns the elapsed fraction into an interpolated fraction
-     * and then into an animated value (from the evaluator. The function is called mostly during
-     * animation updates, but it is also called when the <code>end()</code>
-     * function is called, to set the final value on the property.
-     *
-     * <p>Overrides of this method must call the superclass to perform the calculation
-     * of the animated value.</p>
-     *
-     * @param fraction The elapsed fraction of the animation.
+     * This method tells the object to use appropriate information to extract
+     * starting values for the animation. For example, a AnimatorSet object will pass
+     * this call to its child objects to tell them to set up the values. A
+     * ObjectAnimator object will use the information it has about its target object
+     * and PropertyValuesHolder objects to get the start values for its properties.
+     * An ValueAnimator object will ignore the request since it does not have enough
+     * information (such as a target object) to gather these values.
      */
-    void animateValue(float fraction) {
-        fraction = mInterpolator.getInterpolation(fraction);
-        int numValues = mValues.length;
-        for (int i = 0; i < numValues; ++i) {
-            mValues[i].calculateValue(fraction);
-        }
-        if (mUpdateListeners != null) {
-            int numListeners = mUpdateListeners.size();
-            for (int i = 0; i < numListeners; ++i) {
-                mUpdateListeners.get(i).onAnimationUpdate(this);
-            }
-        }
-    }
-
-    @Override
-    public Animator clone() throws CloneNotSupportedException {
-        final Animator anim = (Animator) super.clone();
-        if (mUpdateListeners != null) {
-            ArrayList<AnimatorUpdateListener> oldListeners = mUpdateListeners;
-            anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
-            int numListeners = oldListeners.size();
-            for (int i = 0; i < numListeners; ++i) {
-                anim.mUpdateListeners.add(oldListeners.get(i));
-            }
-        }
-        anim.mSeekTime = -1;
-        anim.mPlayingBackwards = false;
-        anim.mCurrentIteration = 0;
-        anim.mInitialized = false;
-        anim.mPlayingState = STOPPED;
-        anim.mStartedDelay = false;
-        PropertyValuesHolder[] oldValues = mValues;
-        if (oldValues != null) {
-            int numValues = oldValues.length;
-            anim.mValues = new PropertyValuesHolder[numValues];
-            for (int i = 0; i < numValues; ++i) {
-                anim.mValues[i] = oldValues[i];
-            }
-            anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
-            for (int i = 0; i < numValues; ++i) {
-                PropertyValuesHolder valuesHolder = mValues[i];
-                anim.mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
-            }
-        }
-        return anim;
+    public void setupStartValues() {
     }
 
     /**
-     * Implementors of this interface can add themselves as update listeners
-     * to an <code>Animator</code> instance to receive callbacks on every animation
-     * frame, after the current frame's values have been calculated for that
-     * <code>Animator</code>.
+     * This method tells the object to use appropriate information to extract
+     * ending values for the animation. For example, a AnimatorSet object will pass
+     * this call to its child objects to tell them to set up the values. A
+     * ObjectAnimator object will use the information it has about its target object
+     * and PropertyValuesHolder objects to get the start values for its properties.
+     * An ValueAnimator object will ignore the request since it does not have enough
+     * information (such as a target object) to gather these values.
      */
-    public static interface AnimatorUpdateListener {
+    public void setupEndValues() {
+    }
+
+    /**
+     * Sets the target object whose property will be animated by this animation. Not all subclasses
+     * operate on target objects (for example, {@link ValueAnimator}, but this method
+     * is on the superclass for the convenience of dealing generically with those subclasses
+     * that do handle targets.
+     *
+     * @param target The object being animated
+     */
+    public void setTarget(Object target) {
+    }
+
+    /**
+     * <p>An animation listener receives notifications from an animation.
+     * Notifications indicate animation related events, such as the end or the
+     * repetition of the animation.</p>
+     */
+    public static interface AnimatorListener {
         /**
-         * <p>Notifies the occurrence of another frame of the animation.</p>
+         * <p>Notifies the start of the animation.</p>
+         *
+         * @param animation The started animation.
+         */
+        void onAnimationStart(Animator animation);
+
+        /**
+         * <p>Notifies the end of the animation. This callback is not invoked
+         * for animations with repeat count set to INFINITE.</p>
+         *
+         * @param animation The animation which reached its end.
+         */
+        void onAnimationEnd(Animator animation);
+
+        /**
+         * <p>Notifies the cancellation of the animation. This callback is not invoked
+         * for animations with repeat count set to INFINITE.</p>
+         *
+         * @param animation The animation which was canceled.
+         */
+        void onAnimationCancel(Animator animation);
+
+        /**
+         * <p>Notifies the repetition of the animation.</p>
          *
          * @param animation The animation which was repeated.
          */
-        void onAnimationUpdate(Animator animation);
-
+        void onAnimationRepeat(Animator animation);
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/animation/AnimatableInflater.java b/core/java/android/animation/AnimatorInflater.java
similarity index 83%
rename from core/java/android/animation/AnimatableInflater.java
rename to core/java/android/animation/AnimatorInflater.java
index 88fa77e..0016459 100644
--- a/core/java/android/animation/AnimatableInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -30,7 +30,7 @@
 import java.util.ArrayList;
 
 /**
- * This class is used to instantiate menu XML files into Animatable objects.
+ * This class is used to instantiate menu XML files into Animator objects.
  * <p>
  * For performance reasons, menu inflation relies heavily on pre-processing of
  * XML files that is done at build time. Therefore, it is not currently possible
@@ -38,10 +38,10 @@
  * it only works with an XmlPullParser returned from a compiled resource (R.
  * <em>something</em> file.)
  */
-public class AnimatableInflater {
+public class AnimatorInflater {
 
     /**
-     * These flags are used when parsing Sequencer objects
+     * These flags are used when parsing AnimatorSet objects
      */
     private static final int TOGETHER = 0;
     private static final int SEQUENTIALLY = 1;
@@ -56,20 +56,20 @@
     private static final int VALUE_TYPE_CUSTOM      = 4;
 
     /**
-     * Loads an {@link Animatable} object from a resource
+     * Loads an {@link Animator} object from a resource
      *
      * @param context Application context used to access resources
      * @param id The resource id of the animation to load
-     * @return The animatable object reference by the specified id
+     * @return The animator object reference by the specified id
      * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
      */
-    public static Animatable loadAnimatable(Context context, int id)
+    public static Animator loadAnimator(Context context, int id)
             throws NotFoundException {
 
         XmlResourceParser parser = null;
         try {
             parser = context.getResources().getAnimation(id);
-            return createAnimatableFromXml(context, parser);
+            return createAnimatorFromXml(context, parser);
         } catch (XmlPullParserException ex) {
             Resources.NotFoundException rnf =
                     new Resources.NotFoundException("Can't load animation resource ID #0x" +
@@ -87,18 +87,18 @@
         }
     }
 
-    private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser)
+    private static Animator createAnimatorFromXml(Context c, XmlPullParser parser)
             throws XmlPullParserException, IOException {
 
-        return createAnimatableFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
+        return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
     }
 
-    private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser,
-            AttributeSet attrs, Sequencer parent, int sequenceOrdering)
+    private static Animator createAnimatorFromXml(Context c, XmlPullParser parser,
+            AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)
             throws XmlPullParserException, IOException {
 
-        Animatable anim = null;
-        ArrayList<Animatable> childAnims = null;
+        Animator anim = null;
+        ArrayList<Animator> childAnims = null;
 
         // Make sure we are on a start tag.
         int type;
@@ -113,17 +113,17 @@
 
             String  name = parser.getName();
 
-            if (name.equals("property")) {
-                anim = loadPropertyAnimator(c, attrs);
+            if (name.equals("objectAnimator")) {
+                anim = loadObjectAnimator(c, attrs);
             } else if (name.equals("animator")) {
                 anim = loadAnimator(c, attrs, null);
-            } else if (name.equals("sequencer")) {
-                anim = new Sequencer();
+            } else if (name.equals("set")) {
+                anim = new AnimatorSet();
                 TypedArray a = c.obtainStyledAttributes(attrs,
-                        com.android.internal.R.styleable.Sequencer);
-                int ordering = a.getInt(com.android.internal.R.styleable.Sequencer_ordering,
+                        com.android.internal.R.styleable.AnimatorSet);
+                int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering,
                         TOGETHER);
-                createAnimatableFromXml(c, parser, attrs, (Sequencer) anim,  ordering);
+                createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim,  ordering);
                 a.recycle();
             } else {
                 throw new RuntimeException("Unknown animator name: " + parser.getName());
@@ -131,15 +131,15 @@
 
             if (parent != null) {
                 if (childAnims == null) {
-                    childAnims = new ArrayList<Animatable>();
+                    childAnims = new ArrayList<Animator>();
                 }
                 childAnims.add(anim);
             }
         }
         if (parent != null && childAnims != null) {
-            Animatable[] animsArray = new Animatable[childAnims.size()];
+            Animator[] animsArray = new Animator[childAnims.size()];
             int index = 0;
-            for (Animatable a : childAnims) {
+            for (Animator a : childAnims) {
                 animsArray[index++] = a;
             }
             if (sequenceOrdering == TOGETHER) {
@@ -153,10 +153,10 @@
 
     }
 
-    private static PropertyAnimator loadPropertyAnimator(Context context, AttributeSet attrs)
+    private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs)
             throws NotFoundException {
 
-        PropertyAnimator anim = new PropertyAnimator();
+        ObjectAnimator anim = new ObjectAnimator();
 
         loadAnimator(context, attrs, anim);
 
@@ -179,7 +179,7 @@
      * @param context the application environment
      * @param attrs the set of attributes holding the animation parameters
      */
-    private static Animator loadAnimator(Context context, AttributeSet attrs, Animator anim)
+    private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim)
             throws NotFoundException {
 
         TypedArray a =
@@ -236,7 +236,7 @@
         }
 
         if (anim == null) {
-            anim = new Animator(duration, valueFrom, valueTo);
+            anim = new ValueAnimator(duration, valueFrom, valueTo);
         } else {
             anim.setDuration(duration);
             anim.setValues(valueFrom, valueTo);
@@ -251,7 +251,7 @@
         if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) {
             anim.setRepeatMode(
                     a.getInt(com.android.internal.R.styleable.Animator_repeatMode,
-                            Animator.RESTART));
+                            ValueAnimator.RESTART));
         }
         if (evaluator != null) {
             anim.setEvaluator(evaluator);
diff --git a/core/java/android/animation/AnimatableListenerAdapter.java b/core/java/android/animation/AnimatorListenerAdapter.java
similarity index 68%
rename from core/java/android/animation/AnimatableListenerAdapter.java
rename to core/java/android/animation/AnimatorListenerAdapter.java
index 25a842b..6182389 100644
--- a/core/java/android/animation/AnimatableListenerAdapter.java
+++ b/core/java/android/animation/AnimatorListenerAdapter.java
@@ -16,41 +16,39 @@
 
 package android.animation;
 
-import android.animation.Animatable.AnimatableListener;
-
 /**
- * This adapter class provides empty implementations of the methods from {@link AnimatableListener}.
+ * This adapter class provides empty implementations of the methods from {@link android.animation.Animator.AnimatorListener}.
  * Any custom listener that cares only about a subset of the methods of this listener can
  * simply subclass this adapter class instead of implementing the interface directly.
  */
-public abstract class AnimatableListenerAdapter implements AnimatableListener {
+public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener {
 
     /**
-     * @{inheritdoc}
+     * {@inheritdoc}
      */
     @Override
-    public void onAnimationCancel(Animatable animation) {
+    public void onAnimationCancel(Animator animation) {
     }
 
     /**
-     * @{inheritdoc}
+     * {@inheritdoc}
      */
     @Override
-    public void onAnimationEnd(Animatable animation) {
+    public void onAnimationEnd(Animator animation) {
     }
 
     /**
-     * @{inheritdoc}
+     * {@inheritdoc}
      */
     @Override
-    public void onAnimationRepeat(Animatable animation) {
+    public void onAnimationRepeat(Animator animation) {
     }
 
     /**
-     * @{inheritdoc}
+     * {@inheritdoc}
      */
     @Override
-    public void onAnimationStart(Animatable animation) {
+    public void onAnimationStart(Animator animation) {
     }
 
 }
diff --git a/core/java/android/animation/Sequencer.java b/core/java/android/animation/AnimatorSet.java
similarity index 65%
rename from core/java/android/animation/Sequencer.java
rename to core/java/android/animation/AnimatorSet.java
index 8779b3d..a8385e4 100644
--- a/core/java/android/animation/Sequencer.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -16,34 +16,31 @@
 
 package android.animation;
 
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 
 /**
- * This class plays a set of {@link Animatable} objects in the specified order. Animations
+ * This class plays a set of {@link Animator} objects in the specified order. Animations
  * can be set up to play together, in sequence, or after a specified delay.
  *
- * <p>There are two different approaches to adding animations to a <code>Sequencer</code>:
- * either the {@link Sequencer#playTogether(Animatable[]) playTogether()} or
- * {@link Sequencer#playSequentially(Animatable[]) playSequentially()} methods can be called to add
- * a set of animations all at once, or the {@link Sequencer#play(Animatable)} can be
- * used in conjunction with methods in the {@link android.animation.Sequencer.Builder Builder}
+ * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>:
+ * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
+ * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
+ * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
+ * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
  * class to add animations
  * one by one.</p>
  *
- * <p>It is possible to set up a <code>Sequencer</code> with circular dependencies between
+ * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
  * its animations. For example, an animation a1 could be set up to start before animation a2, a2
  * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
  * result in none of the affected animations being played. Because of this (and because
  * circular dependencies do not make logical sense anyway), circular dependencies
  * should be avoided, and the dependency flow of animations should only be in one direction.
  */
-public final class Sequencer extends Animatable {
+public final class AnimatorSet extends Animator {
 
     /**
      * Internal variables
@@ -54,21 +51,21 @@
 
     /**
      * Tracks animations currently being played, so that we know what to
-     * cancel or end when cancel() or end() is called on this Sequencer
+     * cancel or end when cancel() or end() is called on this AnimatorSet
      */
-    private ArrayList<Animatable> mPlayingSet = new ArrayList<Animatable>();
+    private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
 
     /**
-     * Contains all nodes, mapped to their respective Animatables. When new
-     * dependency information is added for an Animatable, we want to add it
-     * to a single node representing that Animatable, not create a new Node
+     * Contains all nodes, mapped to their respective Animators. When new
+     * dependency information is added for an Animator, we want to add it
+     * to a single node representing that Animator, not create a new Node
      * if one already exists.
      */
-    private HashMap<Animatable, Node> mNodeMap = new HashMap<Animatable, Node>();
+    private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>();
 
     /**
-     * Set of all nodes created for this Sequencer. This list is used upon
-     * starting the sequencer, and the nodes are placed in sorted order into the
+     * Set of all nodes created for this AnimatorSet. This list is used upon
+     * starting the set, and the nodes are placed in sorted order into the
      * sortedNodes collection.
      */
     private ArrayList<Node> mNodes = new ArrayList<Node>();
@@ -87,59 +84,69 @@
      */
     private boolean mNeedsSort = true;
 
-    private SequencerAnimatableListener mSequenceListener = null;
+    private AnimatorSetListener mSetListener = null;
 
     /**
-     * Flag indicating that the Sequencer has been canceled (by calling cancel() or end()).
+     * Flag indicating that the AnimatorSet has been canceled (by calling cancel() or end()).
      * This flag is used to avoid starting other animations when currently-playing
-     * child animations of this Sequencer end.
+     * child animations of this AnimatorSet end.
      */
     boolean mCanceled = false;
 
+    // The amount of time in ms to delay starting the animation after start() is called
+    private long mStartDelay = 0;
+
+
+    // How long the child animations should last in ms. The default value is negative, which
+    // simply means that there is no duration set on the AnimatorSet. When a real duration is
+    // set, it is passed along to the child animations.
+    private long mDuration = -1;
+
+
     /**
-     * Sets up this Sequencer to play all of the supplied animations at the same time.
+     * Sets up this AnimatorSet to play all of the supplied animations at the same time.
      *
-     * @param sequenceItems The animations that will be started simultaneously.
+     * @param items The animations that will be started simultaneously.
      */
-    public void playTogether(Animatable... sequenceItems) {
-        if (sequenceItems != null) {
+    public void playTogether(Animator... items) {
+        if (items != null) {
             mNeedsSort = true;
-            Builder builder = play(sequenceItems[0]);
-            for (int i = 1; i < sequenceItems.length; ++i) {
-                builder.with(sequenceItems[i]);
+            Builder builder = play(items[0]);
+            for (int i = 1; i < items.length; ++i) {
+                builder.with(items[i]);
             }
         }
     }
 
     /**
-     * Sets up this Sequencer to play each of the supplied animations when the
+     * Sets up this AnimatorSet to play each of the supplied animations when the
      * previous animation ends.
      *
-     * @param sequenceItems The aniamtions that will be started one after another.
+     * @param items The aniamtions that will be started one after another.
      */
-    public void playSequentially(Animatable... sequenceItems) {
-        if (sequenceItems != null) {
+    public void playSequentially(Animator... items) {
+        if (items != null) {
             mNeedsSort = true;
-            if (sequenceItems.length == 1) {
-                play(sequenceItems[0]);
+            if (items.length == 1) {
+                play(items[0]);
             } else {
-                for (int i = 0; i < sequenceItems.length - 1; ++i) {
-                    play(sequenceItems[i]).before(sequenceItems[i+1]);
+                for (int i = 0; i < items.length - 1; ++i) {
+                    play(items[i]).before(items[i+1]);
                 }
             }
         }
     }
 
     /**
-     * Returns the current list of child Animatable objects controlled by this
-     * Sequencer. This is a copy of the internal list; modifications to the returned list
-     * will not affect the Sequencer, although changes to the underlying Animatable objects
-     * will affect those objects being managed by the Sequencer.
+     * Returns the current list of child Animator objects controlled by this
+     * AnimatorSet. This is a copy of the internal list; modifications to the returned list
+     * will not affect the AnimatorSet, although changes to the underlying Animator objects
+     * will affect those objects being managed by the AnimatorSet.
      *
-     * @return ArrayList<Animatable> The list of child animations of this Sequencer.
+     * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
      */
-    public ArrayList<Animatable> getChildAnimations() {
-        ArrayList<Animatable> childList = new ArrayList<Animatable>();
+    public ArrayList<Animator> getChildAnimations() {
+        ArrayList<Animator> childList = new ArrayList<Animator>();
         for (Node node : mNodes) {
             childList.add(node.animation);
         }
@@ -148,32 +155,46 @@
 
     /**
      * Sets the target object for all current {@link #getChildAnimations() child animations}
-     * of this Sequencer that take targets ({@link android.animation.PropertyAnimator} and
-     * Sequencer).
+     * of this AnimatorSet that take targets ({@link ObjectAnimator} and
+     * AnimatorSet).
      *
      * @param target The object being animated
      */
+    @Override
     public void setTarget(Object target) {
         for (Node node : mNodes) {
-            Animatable animation = node.animation;
-            if (animation instanceof Sequencer) {
-                ((Sequencer)animation).setTarget(target);
-            } else if (animation instanceof PropertyAnimator) {
-                ((PropertyAnimator)animation).setTarget(target);
+            Animator animation = node.animation;
+            if (animation instanceof AnimatorSet) {
+                ((AnimatorSet)animation).setTarget(target);
+            } else if (animation instanceof ObjectAnimator) {
+                ((ObjectAnimator)animation).setTarget(target);
             }
         }
     }
 
     /**
+     * Sets the Interpolator for all current {@link #getChildAnimations() child animations}
+     * of this AnimatorSet.
+     *
+     * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
+     */
+    @Override
+    public void setInterpolator(Interpolator interpolator) {
+        for (Node node : mNodes) {
+            node.animation.setInterpolator(interpolator);
+        }
+    }
+
+    /**
      * This method creates a <code>Builder</code> object, which is used to
      * set up playing constraints. This initial <code>play()</code> method
      * tells the <code>Builder</code> the animation that is the dependency for
      * the succeeding commands to the <code>Builder</code>. For example,
-     * calling <code>play(a1).with(a2)</code> sets up the Sequence to play
+     * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
      * <code>a1</code> and <code>a2</code> at the same time,
-     * <code>play(a1).before(a2)</code> sets up the Sequence to play
+     * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
      * <code>a1</code> first, followed by <code>a2</code>, and
-     * <code>play(a1).after(a2)</code> sets up the Sequence to play
+     * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
      * <code>a2</code> first, followed by <code>a1</code>.
      *
      * <p>Note that <code>play()</code> is the only way to tell the
@@ -188,11 +209,11 @@
      * @param anim The animation that is the dependency used in later calls to the
      * methods in the returned <code>Builder</code> object. A null parameter will result
      * in a null <code>Builder</code> return value.
-     * @return Builder The object that constructs the sequence based on the dependencies
+     * @return Builder The object that constructs the AnimatorSet based on the dependencies
      * outlined in the calls to <code>play</code> and the other methods in the
      * <code>Builder</code object.
      */
-    public Builder play(Animatable anim) {
+    public Builder play(Animator anim) {
         if (anim != null) {
             mNeedsSort = true;
             return new Builder(anim);
@@ -203,7 +224,7 @@
     /**
      * {@inheritDoc}
      *
-     * <p>Note that canceling a <code>Sequencer</code> also cancels all of the animations that it is
+     * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it is
      * responsible for.</p>
      */
     @SuppressWarnings("unchecked")
@@ -211,9 +232,9 @@
     public void cancel() {
         mCanceled = true;
         if (mListeners != null) {
-            ArrayList<AnimatableListener> tmpListeners =
-                    (ArrayList<AnimatableListener>) mListeners.clone();
-            for (AnimatableListener listener : tmpListeners) {
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            for (AnimatorListener listener : tmpListeners) {
                 listener.onAnimationCancel(this);
             }
         }
@@ -227,7 +248,7 @@
     /**
      * {@inheritDoc}
      *
-     * <p>Note that ending a <code>Sequencer</code> also ends all of the animations that it is
+     * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
      * responsible for.</p>
      */
     @Override
@@ -237,10 +258,10 @@
             // hasn't been started yet - sort the nodes now, then end them
             sortNodes();
             for (Node node : mSortedNodes) {
-                if (mSequenceListener == null) {
-                    mSequenceListener = new SequencerAnimatableListener(this);
+                if (mSetListener == null) {
+                    mSetListener = new AnimatorSetListener(this);
                 }
-                node.animation.addListener(mSequenceListener);
+                node.animation.addListener(mSetListener);
             }
         }
         if (mSortedNodes.size() > 0) {
@@ -251,9 +272,9 @@
     }
 
     /**
-     * Returns true if any of the child animations of this Sequencer have been started and have not
+     * Returns true if any of the child animations of this AnimatorSet have been started and have not
      * yet ended.
-     * @return Whether this Sequencer has been started and has not yet ended.
+     * @return Whether this AnimatorSet has been started and has not yet ended.
      */
     @Override
     public boolean isRunning() {
@@ -266,9 +287,65 @@
     }
 
     /**
+     * The amount of time, in milliseconds, to delay starting the animation after
+     * {@link #start()} is called.
+     *
+     * @return the number of milliseconds to delay running the animation
+     */
+    @Override
+    public long getStartDelay() {
+        return mStartDelay;
+    }
+
+    /**
+     * The amount of time, in milliseconds, to delay starting the animation after
+     * {@link #start()} is called.
+
+     * @param startDelay The amount of the delay, in milliseconds
+     */
+    @Override
+    public void setStartDelay(long startDelay) {
+        mStartDelay = startDelay;
+    }
+
+    /**
+     * Gets the length of each of the child animations of this AnimatorSet. This value may
+     * be less than 0, which indicates that no duration has been set on this AnimatorSet
+     * and each of the child animations will use their own duration.
+     *
+     * @return The length of the animation, in milliseconds, of each of the child
+     * animations of this AnimatorSet.
+     */
+    @Override
+    public long getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Sets the length of each of the current child animations of this AnimatorSet. By default,
+     * each child animation will use its own duration. If the duration is set on the AnimatorSet,
+     * then each child animation inherits this duration.
+     *
+     * @param duration The length of the animation, in milliseconds, of each of the child
+     * animations of this AnimatorSet.
+     */
+    @Override
+    public void setDuration(long duration) {
+        if (duration < 0) {
+            throw new IllegalArgumentException("duration must be a value of zero or greater");
+        }
+        for (Node node : mNodes) {
+            // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
+            // insert "play-after" delays
+            node.animation.setDuration(duration);
+        }
+        mDuration = duration;
+    }
+
+    /**
      * {@inheritDoc}
      *
-     * <p>Starting this <code>Sequencer</code> will, in turn, start the animations for which
+     * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
      * it is responsible. The details of when exactly those animations are started depends on
      * the dependency relationships that have been set up between the animations.
      */
@@ -285,10 +362,10 @@
         // start the animations in the loop directly because we first need to set up
         // dependencies on all of the nodes. For example, we don't want to start an animation
         // when some other animation also wants to start when the first animation begins.
-        ArrayList<Node> nodesToStart = new ArrayList<Node>();
+        final ArrayList<Node> nodesToStart = new ArrayList<Node>();
         for (Node node : mSortedNodes) {
-            if (mSequenceListener == null) {
-                mSequenceListener = new SequencerAnimatableListener(this);
+            if (mSetListener == null) {
+                mSetListener = new AnimatorSetListener(this);
             }
             if (node.dependencies == null || node.dependencies.size() == 0) {
                 nodesToStart.add(node);
@@ -299,28 +376,41 @@
                 }
                 node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
             }
-            node.animation.addListener(mSequenceListener);
+            node.animation.addListener(mSetListener);
         }
         // Now that all dependencies are set up, start the animations that should be started.
-        for (Node node : nodesToStart) {
-            node.animation.start();
-            mPlayingSet.add(node.animation);
+        if (mStartDelay <= 0) {
+            for (Node node : nodesToStart) {
+                node.animation.start();
+                mPlayingSet.add(node.animation);
+            }
+        } else {
+            // TODO: Need to cancel out of the delay appropriately
+            ValueAnimator delayAnim = new ValueAnimator(mStartDelay, 0f, 1f);
+            delayAnim.addListener(new AnimatorListenerAdapter() {
+                public void onAnimationEnd(Animator anim) {
+                    for (Node node : nodesToStart) {
+                        node.animation.start();
+                        mPlayingSet.add(node.animation);
+                    }
+                }
+            });
         }
         if (mListeners != null) {
-            ArrayList<AnimatableListener> tmpListeners =
-                    (ArrayList<AnimatableListener>) mListeners.clone();
-            for (AnimatableListener listener : tmpListeners) {
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            for (AnimatorListener listener : tmpListeners) {
                 listener.onAnimationStart(this);
             }
         }
     }
 
     @Override
-    public Sequencer clone() throws CloneNotSupportedException {
-        final Sequencer anim = (Sequencer) super.clone();
+    public AnimatorSet clone() {
+        final AnimatorSet anim = (AnimatorSet) super.clone();
         /*
          * The basic clone() operation copies all items. This doesn't work very well for
-         * Sequencer, because it will copy references that need to be recreated and state
+         * AnimatorSet, because it will copy references that need to be recreated and state
          * that may not apply. What we need to do now is put the clone in an uninitialized
          * state, with fresh, empty data structures. Then we will build up the nodes list
          * manually, as we clone each Node (and its animation). The clone will then be sorted,
@@ -328,13 +418,13 @@
          */
         anim.mNeedsSort = true;
         anim.mCanceled = false;
-        anim.mPlayingSet = new ArrayList<Animatable>();
-        anim.mNodeMap = new HashMap<Animatable, Node>();
+        anim.mPlayingSet = new ArrayList<Animator>();
+        anim.mNodeMap = new HashMap<Animator, Node>();
         anim.mNodes = new ArrayList<Node>();
         anim.mSortedNodes = new ArrayList<Node>();
 
         // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
-        // One problem is that the old node dependencies point to nodes in the old sequencer.
+        // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
         // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
         HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
         for (Node node : mNodes) {
@@ -347,21 +437,21 @@
             nodeClone.tmpDependencies = null;
             nodeClone.nodeDependents = null;
             nodeClone.nodeDependencies = null;
-            // clear out any listeners that were set up by the sequencer; these will
+            // clear out any listeners that were set up by the AnimatorSet; these will
             // be set up when the clone's nodes are sorted
-            ArrayList<AnimatableListener> cloneListeners = nodeClone.animation.getListeners();
+            ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
             if (cloneListeners != null) {
-                ArrayList<AnimatableListener> listenersToRemove = null;
-                for (AnimatableListener listener : cloneListeners) {
-                    if (listener instanceof SequencerAnimatableListener) {
+                ArrayList<AnimatorListener> listenersToRemove = null;
+                for (AnimatorListener listener : cloneListeners) {
+                    if (listener instanceof AnimatorSetListener) {
                         if (listenersToRemove == null) {
-                            listenersToRemove = new ArrayList<AnimatableListener>();
+                            listenersToRemove = new ArrayList<AnimatorListener>();
                         }
                         listenersToRemove.add(listener);
                     }
                 }
                 if (listenersToRemove != null) {
-                    for (AnimatableListener listener : listenersToRemove) {
+                    for (AnimatorListener listener : listenersToRemove) {
                         cloneListeners.remove(listener);
                     }
                 }
@@ -389,9 +479,9 @@
      * animations. If an animation has multiple dependencies on other animations, then
      * all dependencies must be satisfied before the animation is started.
      */
-    private static class DependencyListener implements AnimatableListener {
+    private static class DependencyListener implements AnimatorListener {
 
-        private Sequencer mSequencer;
+        private AnimatorSet mAnimatorSet;
 
         // The node upon which the dependency is based.
         private Node mNode;
@@ -400,8 +490,8 @@
         // the node
         private int mRule;
 
-        public DependencyListener(Sequencer sequencer, Node node, int rule) {
-            this.mSequencer = sequencer;
+        public DependencyListener(AnimatorSet animatorSet, Node node, int rule) {
+            this.mAnimatorSet = animatorSet;
             this.mNode = node;
             this.mRule = rule;
         }
@@ -411,13 +501,13 @@
          * to prevent follow-on animations from running when some dependency
          * animation is canceled.
          */
-        public void onAnimationCancel(Animatable animation) {
+        public void onAnimationCancel(Animator animation) {
         }
 
         /**
          * An end event is received - see if this is an event we are listening for
          */
-        public void onAnimationEnd(Animatable animation) {
+        public void onAnimationEnd(Animator animation) {
             if (mRule == Dependency.AFTER) {
                 startIfReady(animation);
             }
@@ -426,13 +516,13 @@
         /**
          * Ignore repeat events for now
          */
-        public void onAnimationRepeat(Animatable animation) {
+        public void onAnimationRepeat(Animator animation) {
         }
 
         /**
          * A start event is received - see if this is an event we are listening for
          */
-        public void onAnimationStart(Animatable animation) {
+        public void onAnimationStart(Animator animation) {
             if (mRule == Dependency.WITH) {
                 startIfReady(animation);
             }
@@ -444,9 +534,9 @@
          * the animation.
          * @param dependencyAnimation the animation that sent the event.
          */
-        private void startIfReady(Animatable dependencyAnimation) {
-            if (mSequencer.mCanceled) {
-                // if the parent Sequencer was canceled, then don't start any dependent anims
+        private void startIfReady(Animator dependencyAnimation) {
+            if (mAnimatorSet.mCanceled) {
+                // if the parent AnimatorSet was canceled, then don't start any dependent anims
                 return;
             }
             Dependency dependencyToRemove = null;
@@ -464,37 +554,37 @@
             if (mNode.tmpDependencies.size() == 0) {
                 // all dependencies satisfied: start the animation
                 mNode.animation.start();
-                mSequencer.mPlayingSet.add(mNode.animation);
+                mAnimatorSet.mPlayingSet.add(mNode.animation);
             }
         }
 
     }
 
-    private class SequencerAnimatableListener implements AnimatableListener {
+    private class AnimatorSetListener implements AnimatorListener {
 
-        private Sequencer mSequencer;
+        private AnimatorSet mAnimatorSet;
 
-        SequencerAnimatableListener(Sequencer sequencer) {
-            mSequencer = sequencer;
+        AnimatorSetListener(AnimatorSet animatorSet) {
+            mAnimatorSet = animatorSet;
         }
 
-        public void onAnimationCancel(Animatable animation) {
+        public void onAnimationCancel(Animator animation) {
             if (mPlayingSet.size() == 0) {
                 if (mListeners != null) {
-                    for (AnimatableListener listener : mListeners) {
-                        listener.onAnimationCancel(mSequencer);
+                    for (AnimatorListener listener : mListeners) {
+                        listener.onAnimationCancel(mAnimatorSet);
                     }
                 }
             }
         }
 
         @SuppressWarnings("unchecked")
-        public void onAnimationEnd(Animatable animation) {
+        public void onAnimationEnd(Animator animation) {
             animation.removeListener(this);
             mPlayingSet.remove(animation);
-            Node animNode = mSequencer.mNodeMap.get(animation);
+            Node animNode = mAnimatorSet.mNodeMap.get(animation);
             animNode.done = true;
-            ArrayList<Node> sortedNodes = mSequencer.mSortedNodes;
+            ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
             boolean allDone = true;
             for (Node node : sortedNodes) {
                 if (!node.done) {
@@ -504,23 +594,23 @@
             }
             if (allDone) {
                 // If this was the last child animation to end, then notify listeners that this
-                // sequencer has ended
+                // AnimatorSet has ended
                 if (mListeners != null) {
-                    ArrayList<AnimatableListener> tmpListeners =
-                            (ArrayList<AnimatableListener>) mListeners.clone();
-                    for (AnimatableListener listener : tmpListeners) {
-                        listener.onAnimationEnd(mSequencer);
+                    ArrayList<AnimatorListener> tmpListeners =
+                            (ArrayList<AnimatorListener>) mListeners.clone();
+                    for (AnimatorListener listener : tmpListeners) {
+                        listener.onAnimationEnd(mAnimatorSet);
                     }
                 }
             }
         }
 
         // Nothing to do
-        public void onAnimationRepeat(Animatable animation) {
+        public void onAnimationRepeat(Animator animation) {
         }
 
         // Nothing to do
-        public void onAnimationStart(Animatable animation) {
+        public void onAnimationStart(Animator animation) {
         }
 
     }
@@ -564,7 +654,7 @@
             mNeedsSort = false;
             if (mSortedNodes.size() != mNodes.size()) {
                 throw new IllegalStateException("Circular dependencies cannot exist"
-                        + " in Sequencer");
+                        + " in AnimatorSet");
             }
         } else {
             // Doesn't need sorting, but still need to add in the nodeDependencies list
@@ -608,13 +698,13 @@
     }
 
     /**
-     * A Node is an embodiment of both the Animatable that it wraps as well as
+     * A Node is an embodiment of both the Animator that it wraps as well as
      * any dependencies that are associated with that Animation. This includes
      * both dependencies upon other nodes (in the dependencies list) as
      * well as dependencies of other nodes upon this (in the nodeDependents list).
      */
     private static class Node implements Cloneable {
-        public Animatable animation;
+        public Animator animation;
 
         /**
          *  These are the dependencies that this node's animation has on other
@@ -629,8 +719,8 @@
          * But we also use the list to keep track of when multiple dependencies are satisfied,
          * but removing each dependency as it is satisfied. We do not want to remove
          * the dependency itself from the list, because we need to retain that information
-         * if the sequencer is launched in the future. So we create a copy of the dependency
-         * list when the sequencer starts and use this tmpDependencies list to track the
+         * if the AnimatorSet is launched in the future. So we create a copy of the dependency
+         * list when the AnimatorSet starts and use this tmpDependencies list to track the
          * list of satisfied dependencies.
          */
         public ArrayList<Dependency> tmpDependencies = null;
@@ -650,8 +740,8 @@
 
         /**
          * Flag indicating whether the animation in this node is finished. This flag
-         * is used by Sequencer to check, as each animation ends, whether all child animations
-         * are done and it's time to send out an end event for the entire Sequencer.
+         * is used by AnimatorSet to check, as each animation ends, whether all child animations
+         * are done and it's time to send out an end event for the entire AnimatorSet.
          */
         public boolean done = false;
 
@@ -662,7 +752,7 @@
          *
          * @param animation The animation that the Node encapsulates.
          */
-        public Node(Animatable animation) {
+        public Node(Animator animation) {
             this.animation = animation;
         }
 
@@ -688,54 +778,58 @@
         }
 
         @Override
-        public Node clone() throws CloneNotSupportedException {
-            Node node = (Node) super.clone();
-            node.animation = (Animatable) animation.clone();
-            return node;
+        public Node clone() {
+            try {
+                Node node = (Node) super.clone();
+                node.animation = (Animator) animation.clone();
+                return node;
+            } catch (CloneNotSupportedException e) {
+               throw new AssertionError();
+            }
         }
     }
 
     /**
      * The <code>Builder</code> object is a utility class to facilitate adding animations to a
-     * <code>Sequencer</code> along with the relationships between the various animations. The
+     * <code>AnimatorSet</code> along with the relationships between the various animations. The
      * intention of the <code>Builder</code> methods, along with the {@link
-     * Sequencer#play(Animatable) play()} method of <code>Sequencer</code> is to make it possible to
+     * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible to
      * express the dependency relationships of animations in a natural way. Developers can also use
-     * the {@link Sequencer#playTogether(Animatable[]) playTogether()} and {@link
-     * Sequencer#playSequentially(Animatable[]) playSequentially()} methods if these suit the need,
-     * but it might be easier in some situations to express the sequence of animations in pairs.
+     * the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
+     * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
+     * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
      * <p/>
      * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
-     * internally via a call to {@link Sequencer#play(Animatable)}.</p>
+     * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
      * <p/>
-     * <p>For example, this sets up a Sequencer to play anim1 and anim2 at the same time, anim3 to
+     * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
      * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
      * <pre>
-     *     Sequencer s = new Sequencer();
+     *     AnimatorSet s = new AnimatorSet();
      *     s.play(anim1).with(anim2);
      *     s.play(anim2).before(anim3);
      *     s.play(anim4).after(anim3);
      * </pre>
      * <p/>
-     * <p>Note in the example that both {@link Builder#before(Animatable)} and {@link
-     * Builder#after(Animatable)} are used. These are just different ways of expressing the same
+     * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
+     * Builder#after(Animator)} are used. These are just different ways of expressing the same
      * relationship and are provided to make it easier to say things in a way that is more natural,
      * depending on the situation.</p>
      * <p/>
      * <p>It is possible to make several calls into the same <code>Builder</code> object to express
      * multiple relationships. However, note that it is only the animation passed into the initial
-     * {@link Sequencer#play(Animatable)} method that is the dependency in any of the successive
+     * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
      * calls to the <code>Builder</code> object. For example, the following code starts both anim2
      * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
      * anim3:
      * <pre>
-     *   Sequencer s = new Sequencer();
+     *   AnimatorSet s = new AnimatorSet();
      *   s.play(anim1).before(anim2).before(anim3);
      * </pre>
      * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
      * relationship correctly:</p>
      * <pre>
-     *   Sequencer s = new Sequencer();
+     *   AnimatorSet s = new AnimatorSet();
      *   s.play(anim1).before(anim2);
      *   s.play(anim2).before(anim3);
      * </pre>
@@ -743,26 +837,26 @@
      * <p>Note that it is possible to express relationships that cannot be resolved and will not
      * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
      * sense. In general, circular dependencies like this one (or more indirect ones where a depends
-     * on b, which depends on c, which depends on a) should be avoided. Only create sequences that
-     * can boil down to a simple, one-way relationship of animations starting with, before, and
+     * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
+     * that can boil down to a simple, one-way relationship of animations starting with, before, and
      * after other, different, animations.</p>
      */
     public class Builder {
 
         /**
          * This tracks the current node being processed. It is supplied to the play() method
-         * of Sequencer and passed into the constructor of Builder.
+         * of AnimatorSet and passed into the constructor of Builder.
          */
         private Node mCurrentNode;
 
         /**
-         * package-private constructor. Builders are only constructed by Sequencer, when the
+         * package-private constructor. Builders are only constructed by AnimatorSet, when the
          * play() method is called.
          *
          * @param anim The animation that is the dependency for the other animations passed into
          * the other methods of this Builder object.
          */
-        Builder(Animatable anim) {
+        Builder(Animator anim) {
             mCurrentNode = mNodeMap.get(anim);
             if (mCurrentNode == null) {
                 mCurrentNode = new Node(anim);
@@ -773,12 +867,12 @@
 
         /**
          * Sets up the given animation to play at the same time as the animation supplied in the
-         * {@link Sequencer#play(Animatable)} call that created this <code>Builder</code> object.
+         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
          *
          * @param anim The animation that will play when the animation supplied to the
-         * {@link Sequencer#play(Animatable)} method starts.
+         * {@link AnimatorSet#play(Animator)} method starts.
          */
-        public void with(Animatable anim) {
+        public void with(Animator anim) {
             Node node = mNodeMap.get(anim);
             if (node == null) {
                 node = new Node(anim);
@@ -791,13 +885,13 @@
 
         /**
          * Sets up the given animation to play when the animation supplied in the
-         * {@link Sequencer#play(Animatable)} call that created this <code>Builder</code> object
+         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
          * ends.
          *
          * @param anim The animation that will play when the animation supplied to the
-         * {@link Sequencer#play(Animatable)} method ends.
+         * {@link AnimatorSet#play(Animator)} method ends.
          */
-        public void before(Animatable anim) {
+        public void before(Animator anim) {
             Node node = mNodeMap.get(anim);
             if (node == null) {
                 node = new Node(anim);
@@ -810,13 +904,13 @@
 
         /**
          * Sets up the given animation to play when the animation supplied in the
-         * {@link Sequencer#play(Animatable)} call that created this <code>Builder</code> object
+         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
          * to start when the animation supplied in this method call ends.
          *
          * @param anim The animation whose end will cause the animation supplied to the
-         * {@link Sequencer#play(Animatable)} method to play.
+         * {@link AnimatorSet#play(Animator)} method to play.
          */
-        public void after(Animatable anim) {
+        public void after(Animator anim) {
             Node node = mNodeMap.get(anim);
             if (node == null) {
                 node = new Node(anim);
@@ -829,15 +923,15 @@
 
         /**
          * Sets up the animation supplied in the
-         * {@link Sequencer#play(Animatable)} call that created this <code>Builder</code> object
+         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
          * to play when the given amount of time elapses.
          *
          * @param delay The number of milliseconds that should elapse before the
          * animation starts.
          */
         public void after(long delay) {
-            // setup dummy Animator just to run the clock
-            after(new Animator(delay, 0f, 1f));
+            // setup dummy ValueAnimator just to run the clock
+            after(new ValueAnimator(delay, 0f, 1f));
         }
 
     }
diff --git a/core/java/android/animation/Keyframe.java b/core/java/android/animation/Keyframe.java
index e2800b3..192ba5c 100644
--- a/core/java/android/animation/Keyframe.java
+++ b/core/java/android/animation/Keyframe.java
@@ -20,13 +20,13 @@
 
 /**
  * This class holds a time/value pair for an animation. The Keyframe class is used
- * by {@link Animator} to define the values that the animation target will have over the course
+ * by {@link ValueAnimator} to define the values that the animation target will have over the course
  * of the animation. As the time proceeds from one keyframe to the other, the value of the
  * target object will animate between the value at the previous keyframe and the value at the
  * next keyframe. Each keyframe also holds an option {@link android.view.animation.Interpolator}
  * object, which defines the time interpolation over the intervalue preceding the keyframe.
  */
-public class Keyframe {
+public class Keyframe implements Cloneable {
     /**
      * The time at which mValue will hold true.
      */
@@ -59,7 +59,7 @@
      * the time in this keyframe, and the the value animated from as the time passes the time in
      * this keyframe.
      * @param valueType The type of the <code>value</code> object. This is used by the
-     * {@link #getValue()} functionm, which is queried by {@link Animator} to determine
+     * {@link #getValue()} functionm, which is queried by {@link ValueAnimator} to determine
      * the type of {@link TypeEvaluator} to use to interpolate between values.
      */
     private Keyframe(float fraction, Object value, Class valueType) {
@@ -81,7 +81,55 @@
      * this keyframe.
      */
     public Keyframe(float fraction, Object value) {
-        this(fraction, value, Object.class);
+        this(fraction, value, (value != null) ? value.getClass() : Object.class);
+    }
+
+    /**
+     * Constructs a Keyframe object with the given time and float value. The time defines the
+     * time, as a proportion of an overall animation's duration, at which the value will hold true
+     * for the animation. The value for the animation between keyframes will be calculated as
+     * an interpolation between the values at those keyframes.
+     *
+     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+     * of time elapsed of the overall animation duration.
+     * @param value The value that the object will animate to as the animation time approaches
+     * the time in this keyframe, and the the value animated from as the time passes the time in
+     * this keyframe.
+     */
+    public Keyframe(float fraction, Float value) {
+        this(fraction, value, Float.class);
+    }
+
+    /**
+     * Constructs a Keyframe object with the given time and integer value. The time defines the
+     * time, as a proportion of an overall animation's duration, at which the value will hold true
+     * for the animation. The value for the animation between keyframes will be calculated as
+     * an interpolation between the values at those keyframes.
+     *
+     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+     * of time elapsed of the overall animation duration.
+     * @param value The value that the object will animate to as the animation time approaches
+     * the time in this keyframe, and the the value animated from as the time passes the time in
+     * this keyframe.
+     */
+    public Keyframe(float fraction, Integer value) {
+        this(fraction, value, Integer.class);
+    }
+
+    /**
+     * Constructs a Keyframe object with the given time and double value. The time defines the
+     * time, as a proportion of an overall animation's duration, at which the value will hold true
+     * for the animation. The value for the animation between keyframes will be calculated as
+     * an interpolation between the values at those keyframes.
+     *
+     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+     * of time elapsed of the overall animation duration.
+     * @param value The value that the object will animate to as the animation time approaches
+     * the time in this keyframe, and the the value animated from as the time passes the time in
+     * this keyframe.
+     */
+    public Keyframe(float fraction, Double value) {
+        this(fraction, value, Double.class);
     }
 
     /**
@@ -191,7 +239,7 @@
     }
 
     /**
-     * Gets the type of keyframe. This information is used by Animator to determine the type of
+     * Gets the type of keyframe. This information is used by ValueAnimator to determine the type of
      * {@link TypeEvaluator} to use when calculating values between keyframes. The type is based
      * on the type of Keyframe created.
      *
@@ -200,4 +248,11 @@
     public Class getType() {
         return mValueType;
     }
+
+    @Override
+    public Keyframe clone() {
+        Keyframe kfClone = new Keyframe(mFraction, mValue, mValueType);
+        kfClone.setInterpolator(mInterpolator);
+        return kfClone;
+    }
 }
diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java
index d144b9c..af47a15 100644
--- a/core/java/android/animation/KeyframeSet.java
+++ b/core/java/android/animation/KeyframeSet.java
@@ -21,7 +21,7 @@
 import android.view.animation.Interpolator;
 
 /**
- * This class holds a collection of Keyframe objects and is called by Animator to calculate
+ * This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
  * values between those keyframes for a given animation. The class internal to the animation
  * package because it is an implementation detail of how Keyframes are stored and used.
  */
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
new file mode 100644
index 0000000..d1bc9bd
--- /dev/null
+++ b/core/java/android/animation/LayoutTransition.java
@@ -0,0 +1,778 @@
+/*
+ * 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.animation;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This class enables automatic animations on layout changes in ViewGroup objects. To enable
+ * transitions for a layout container, create a LayoutTransition object and set it on any
+ * ViewGroup by calling {@link ViewGroup#setLayoutTransition(LayoutTransition)}. This will cause
+ * default animations to run whenever items are added to or removed from that container. To specify
+ * custom animations, use the {@link LayoutTransition#setAnimator(int, Animator)
+ * setAnimator()} method.
+ *
+ * <p>One of the core concepts of these transition animations is that there are two core
+ * changes that cause the transition and four different animations that run because of
+ * those changes. The changes that trigger the transition are items being added to a container
+ * (referred to as an "appearing" transition) or removed from a container (also known as
+ * "disappearing"). The animations that run due to those events are one that animates
+ * items being added, one that animates items being removed, and two that animate the other
+ * items in the container that change due to the add/remove occurrence. Users of
+ * the transition may want different animations for the changing items depending on whether
+ * they are changing due to anappearing or disappearing event, so there is one animation for
+ * each of these variations of the changing event. Most of the API of this class is concerned
+ * with setting up the basic properties of the animations used in these four situations,
+ * or with setting up custom animations for any or all of the four.</p>
+ *
+ * <p>The animations specified for the transition, both the defaults and any custom animations
+ * set on the transition object, are templates only. That is, these animations exist to hold the
+ * basic animation properties, such as the duration, start delay, and properties being animated.
+ * But the actual target object, as well as the start and end values for those properties, are
+ * set automatically in the process of setting up the transition each time it runs. Each of the
+ * animations is cloned from the original copy and the clone is then populated with the dynamic
+ * values of the target being animated (such as one of the items in a layout container that is
+ * moving as a result of the layout event) as well as the values that are changing (such as the
+ * position and size of that object). The actual values that are pushed to each animation
+ * depends on what properties are specified for the animation. For example, the default
+ * CHANGE_APPEARING animation animates <code>left</code>, <code>top</code>, <code>right</code>,
+ * and <code>bottom</code>. Values for these properties are updated with the pre- and post-layout
+ * values when the transition begins. Custom animations will be similarly populated with
+ * the target and values being animated, assuming they use ObjectAnimator objects with
+ * property names that are known on the target object.</p>
+ */
+public class LayoutTransition {
+
+    /**
+     * A flag indicating the animation that runs on those items that are changing
+     * due to a new item appearing in the container.
+     */
+    public static final int CHANGE_APPEARING = 0;
+
+    /**
+     * A flag indicating the animation that runs on those items that are changing
+     * due to a new item disappearing from the container.
+     */
+    public static final int CHANGE_DISAPPEARING = 1;
+
+    /**
+     * A flag indicating the animation that runs on those items that are changing
+     * due to a new item appearing in the container.
+     */
+    public static final int APPEARING = 2;
+
+    /**
+     * A flag indicating the animation that runs on those items that are changing
+     * due to a new item appearing in the container.
+     */
+    public static final int DISAPPEARING = 3;
+
+    /**
+     * These variables hold the animations that are currently used to run the transition effects.
+     * These animations are set to defaults, but can be changed to custom animations by
+     * calls to setAnimator().
+     */
+    private Animator mDisappearingAnim = null;
+    private Animator mAppearingAnim = null;
+    private Animator mChangingAppearingAnim = null;
+    private Animator mChangingDisappearingAnim = null;
+
+    /**
+     * These are the default animations, defined in the constructor, that will be used
+     * unless the user specifies custom animations.
+     */
+    private static ObjectAnimator defaultChangeIn;
+    private static ObjectAnimator defaultChangeOut;
+    private static ObjectAnimator defaultFadeIn;
+    private static ObjectAnimator defaultFadeOut;
+
+    /**
+     * The default duration used by all animations.
+     */
+    private static long DEFAULT_DURATION = 300;
+
+    /**
+     * The durations of the four different animations
+     */
+    private long mChangingAppearingDuration = DEFAULT_DURATION;
+    private long mChangingDisappearingDuration = DEFAULT_DURATION;
+    private long mAppearingDuration = DEFAULT_DURATION;
+    private long mDisappearingDuration = DEFAULT_DURATION;
+
+    /**
+     * The start delays of the four different animations. Note that the default behavior of
+     * the appearing item is the default duration, since it should wait for the items to move
+     * before fading it. Same for the changing animation when disappearing; it waits for the item
+     * to fade out before moving the other items.
+     */
+    private long mAppearingDelay = DEFAULT_DURATION;
+    private long mDisappearingDelay = 0;
+    private long mChangingAppearingDelay = 0;
+    private long mChangingDisappearingDelay = DEFAULT_DURATION;
+
+    /**
+     * The inter-animation delays used on the two changing animations
+     */
+    private long mChangingAppearingStagger = 0;
+    private long mChangingDisappearingStagger = 0;
+
+    /**
+     * The default interpolators used for the animations
+     */
+    private Interpolator mAppearingInterpolator = new AccelerateDecelerateInterpolator();
+    private Interpolator mDisappearingInterpolator = new AccelerateDecelerateInterpolator();
+    private Interpolator mChangingAppearingInterpolator = new DecelerateInterpolator();
+    private Interpolator mChangingDisappearingInterpolator = new DecelerateInterpolator();
+
+    /**
+     * This hashmap is used to store the animations that are currently running as part of
+     * the transition. The reason for this is that a further layout event should cause
+     * existing animations to stop where they are prior to starting new animations. So
+     * we cache all of the current animations in this map for possible cancellation on
+     * another layout event.
+     */
+    private HashMap<View, Animator> currentAnimations = new HashMap<View, Animator>();
+
+    /**
+     * This hashmap is used to track the listeners that have been added to the children of
+     * a container. When a layout change occurs, an animation is created for each View, so that
+     * the pre-layout values can be cached in that animation. Then a listener is added to the
+     * view to see whether the layout changes the bounds of that view. If so, the animation
+     * is set with the final values and then run. If not, the animation is not started. When
+     * the process of setting up and running all appropriate animations is done, we need to
+     * remove these listeners and clear out the map.
+     */
+    private HashMap<View, View.OnLayoutChangeListener> layoutChangeListenerMap =
+            new HashMap<View, View.OnLayoutChangeListener>();
+
+    /**
+     * Used to track the current delay being assigned to successive animations as they are
+     * started. This value is incremented for each new animation, then zeroed before the next
+     * transition begins.
+     */
+    private long staggerDelay;
+
+    /**
+     * The set of listeners that should be notified when APPEARING/DISAPPEARING transitions
+     * start and end.
+     */
+    private ArrayList<TransitionListener> mListeners;
+
+
+    /**
+     * Constructs a LayoutTransition object. By default, the object will listen to layout
+     * events on any ViewGroup that it is set on and will run default animations for each
+     * type of layout event.
+     */
+    public LayoutTransition() {
+        if (defaultChangeIn == null) {
+            // "left" is just a placeholder; we'll put real properties/values in when needed
+            PropertyValuesHolder<Integer> pvhLeft = new PropertyValuesHolder<Integer>("left", 0, 1);
+            PropertyValuesHolder<Integer> pvhTop = new PropertyValuesHolder<Integer>("top", 0, 1);
+            PropertyValuesHolder<Integer> pvhRight = new PropertyValuesHolder<Integer>("right", 0, 1);
+            PropertyValuesHolder<Integer> pvhBottom = new PropertyValuesHolder<Integer>("bottom", 0, 1);
+            defaultChangeIn = new ObjectAnimator<PropertyValuesHolder>(DEFAULT_DURATION, this,
+                    pvhLeft, pvhTop, pvhRight, pvhBottom);
+            defaultChangeIn.setStartDelay(mChangingAppearingDelay);
+            defaultChangeIn.setInterpolator(mChangingAppearingInterpolator);
+            defaultChangeOut = defaultChangeIn.clone();
+            defaultChangeOut.setStartDelay(mChangingDisappearingDelay);
+            defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator);
+            defaultFadeIn =
+                    new ObjectAnimator<Float>(DEFAULT_DURATION, this, "alpha", 0f, 1f);
+            defaultFadeIn.setStartDelay(mAppearingDelay);
+            defaultFadeIn.setInterpolator(mAppearingInterpolator);
+            defaultFadeOut =
+                    new ObjectAnimator<Float>(DEFAULT_DURATION, this, "alpha", 1f, 0f);
+            defaultFadeOut.setStartDelay(mDisappearingDelay);
+            defaultFadeOut.setInterpolator(mDisappearingInterpolator);
+        }
+        mChangingAppearingAnim = defaultChangeIn;
+        mChangingDisappearingAnim = defaultChangeOut;
+        mAppearingAnim = defaultFadeIn;
+        mDisappearingAnim = defaultFadeOut;
+    }
+
+    /**
+     * Sets the duration to be used by all animations of this transition object. If you want to
+     * set the duration of just one of the animations in particular, use the
+     * {@link #setDuration(int, long)} method.
+     *
+     * @param duration The length of time, in milliseconds, that the transition animations
+     * should last.
+     */
+    public void setDuration(long duration) {
+        mChangingAppearingDuration = duration;
+        mChangingDisappearingDuration = duration;
+        mAppearingDuration = duration;
+        mDisappearingDuration = duration;
+    }
+
+    /**
+     * Sets the start delay on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose start delay
+     * is being set.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose start
+     * delay is being set.
+     * @param delay The length of time, in milliseconds, to delay before starting the animation.
+     * @see Animator#setStartDelay(long)
+     */
+    public void setStartDelay(int transitionType, long delay) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingDelay = delay;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingDelay = delay;
+                break;
+            case APPEARING:
+                mAppearingDelay = delay;
+                break;
+            case DISAPPEARING:
+                mDisappearingDelay = delay;
+                break;
+        }
+    }
+
+    /**
+     * Gets the start delay on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose start delay
+     * is returned.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose start
+     * delay is returned.
+     * @return long The start delay of the specified animation.
+     * @see Animator#getStartDelay()
+     */
+    public long getStartDelay(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                return mChangingAppearingDuration;
+            case CHANGE_DISAPPEARING:
+                return mChangingDisappearingDuration;
+            case APPEARING:
+                return mAppearingDuration;
+            case DISAPPEARING:
+                return mDisappearingDuration;
+        }
+        // shouldn't reach here
+        return 0;
+    }
+
+    /**
+     * Sets the duration on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose duration
+     * is being set.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is being set.
+     * @param duration The length of time, in milliseconds, that the specified animation should run.
+     * @see Animator#setDuration(long)
+     */
+    public void setDuration(int transitionType, long duration) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingDuration = duration;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingDuration = duration;
+                break;
+            case APPEARING:
+                mAppearingDuration = duration;
+                break;
+            case DISAPPEARING:
+                mDisappearingDuration = duration;
+                break;
+        }
+    }
+
+    /**
+     * Gets the duration on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose duration
+     * is returned.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is returned.
+     * @return long The duration of the specified animation.
+     * @see Animator#getDuration()
+     */
+    public long getDuration(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                return mChangingAppearingDuration;
+            case CHANGE_DISAPPEARING:
+                return mChangingDisappearingDuration;
+            case APPEARING:
+                return mAppearingDuration;
+            case DISAPPEARING:
+                return mDisappearingDuration;
+        }
+        // shouldn't reach here
+        return 0;
+    }
+
+    /**
+     * Sets the length of time to delay between starting each animation during one of the
+     * CHANGE animations.
+     *
+     * @param transitionType A value of {@link #CHANGE_APPEARING} or @link #CHANGE_DISAPPEARING}.
+     * @param duration The length of time, in milliseconds, to delay before launching the next
+     * animation in the sequence.
+     */
+    public void setStagger(int transitionType, long duration) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingStagger = duration;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingStagger = duration;
+                break;
+            // noop other cases
+        }
+    }
+
+    /**
+     * Tets the length of time to delay between starting each animation during one of the
+     * CHANGE animations.
+     *
+     * @param transitionType A value of {@link #CHANGE_APPEARING} or @link #CHANGE_DISAPPEARING}.
+     * @return long The length of time, in milliseconds, to delay before launching the next
+     * animation in the sequence.
+     */
+    public long getStagger(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                return mChangingAppearingStagger;
+            case CHANGE_DISAPPEARING:
+                return mChangingDisappearingStagger;
+        }
+        // shouldn't reach here
+        return 0;
+    }
+
+    /**
+     * Sets the interpolator on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose interpolator
+     * is being set.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is being set.
+     * @param interpolator The interpolator that the specified animation should use.
+     * @see Animator#setInterpolator(android.view.animation.Interpolator)
+     */
+    public void setInterpolator(int transitionType, Interpolator interpolator) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingInterpolator = interpolator;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingInterpolator = interpolator;
+                break;
+            case APPEARING:
+                mAppearingInterpolator = interpolator;
+                break;
+            case DISAPPEARING:
+                mDisappearingInterpolator = interpolator;
+                break;
+        }
+    }
+
+    /**
+     * Gets the interpolator on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose interpolator
+     * is returned.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is being set.
+     * @return Interpolator The interpolator that the specified animation uses.
+     * @see Animator#setInterpolator(android.view.animation.Interpolator)
+     */
+    public Interpolator getInterpolator(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                return mChangingAppearingInterpolator;
+            case CHANGE_DISAPPEARING:
+                return mChangingDisappearingInterpolator;
+            case APPEARING:
+                return mAppearingInterpolator;
+            case DISAPPEARING:
+                return mDisappearingInterpolator;
+        }
+        // shouldn't reach here
+        return null;
+    }
+
+    /**
+     * Sets the animation used during one of the transition types that may run. Any
+     * Animator object can be used, but to be most useful in the context of layout
+     * transitions, the animation should either be a ObjectAnimator or a AnimatorSet
+     * of animations including PropertyAnimators. Also, these ObjectAnimator objects
+     * should be able to get and set values on their target objects automatically. For
+     * example, a ObjectAnimator that animates the property "left" is able to set and get the
+     * <code>left</code> property from the View objects being animated by the layout
+     * transition. The transition works by setting target objects and properties
+     * dynamically, according to the pre- and post-layoout values of those objects, so
+     * having animations that can handle those properties appropriately will work best
+     * for custom animation. The dynamic setting of values is only the case for the
+     * CHANGE animations; the APPEARING and DISAPPEARING animations are simply run with
+     * the values they have.
+     *
+     * <p>It is also worth noting that any and all animations (and their underlying
+     * PropertyValuesHolder objects) will have their start and end values set according
+     * to the pre- and post-layout values. So, for example, a custom animation on "alpha"
+     * as the CHANGE_APPEARING animation will inherit the real value of alpha on the target
+     * object (presumably 1) as its starting and ending value when the animation begins.
+     * Animations which need to use values at the beginning and end that may not match the
+     * values queried when the transition begins may need to use a different mechanism
+     * than a standard ObjectAnimator object.</p>
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is being set.
+     * @param animator The animation being assigned.
+     */
+    public void setAnimator(int transitionType, Animator animator) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingAnim = (animator != null) ? animator : defaultChangeIn;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingAnim = (animator != null) ? animator : defaultChangeOut;
+                break;
+            case APPEARING:
+                mAppearingAnim = (animator != null) ? animator : defaultFadeIn;
+                break;
+            case DISAPPEARING:
+                mDisappearingAnim = (animator != null) ? animator : defaultFadeOut;
+                break;
+        }
+    }
+
+    /**
+     * Gets the animation used during one of the transition types that may run.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is being set.
+     * @return Animator The animation being used for the given transition type.
+     * @see #setAnimator(int, Animator)
+     */
+    public Animator getAnimator(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                return mChangingAppearingAnim;
+            case CHANGE_DISAPPEARING:
+                return mChangingDisappearingAnim;
+            case APPEARING:
+                return mAppearingAnim;
+            case DISAPPEARING:
+                return mDisappearingAnim;
+        }
+        // shouldn't reach here
+        return null;
+    }
+
+    /**
+     * This function sets up runs animations on all of the views that change during layout.
+     * For every child in the parent, we create a change animation of the appropriate
+     * type (appearing or disappearing) and ask it to populate its start values from its
+     * target view. We add layout listeners to all child views and listen for changes. For
+     * those views that change, we populate the end values for those animations and start them.
+     * Animations are not run on unchanging views.
+     *
+     * @param parent The container which is undergoing an appearing or disappearing change.
+     * @param newView The view being added to or removed from the parent.
+     * @param changeReason A value of APPEARING or DISAPPEARING, indicating whether the
+     * transition is occuring because an item is being added to or removed from the parent.
+     */
+    private void runChangeTransition(final ViewGroup parent, View newView, final int changeReason) {
+        // reset the inter-animation delay, in case we use it later
+        staggerDelay = 0;
+
+        final ViewTreeObserver observer = parent.getViewTreeObserver(); // used for later cleanup
+        int numChildren = parent.getChildCount();
+
+        for (int i = 0; i < numChildren; ++i) {
+            final View child = parent.getChildAt(i);
+
+            // only animate the views not being added or removed
+            if (child != newView) {
+
+                // If there's an animation running on this view already, cancel it
+                Animator currentAnimation = currentAnimations.get(child);
+                if (currentAnimation != null) {
+                    currentAnimation.cancel();
+                    currentAnimations.remove(child);
+                }
+
+                // Make a copy of the appropriate animation
+                final Animator anim = (changeReason == APPEARING) ?
+                        mChangingAppearingAnim.clone() :
+                        mChangingDisappearingAnim.clone();
+
+                // Set the target object for the animation
+                anim.setTarget(child);
+
+                // A ObjectAnimator (or AnimatorSet of them) can extract start values from
+                // its target object
+                anim.setupStartValues();
+
+                // Add a listener to track layout changes on this view. If we don't get a callback,
+                // then there's nothing to animate.
+                View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() {
+                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
+
+                        // Cache the animation in case we need to cancel it later
+                        currentAnimations.put(child, anim);
+
+                        // Tell the animation to extract end values from the changed object
+                        anim.setupEndValues();
+
+                        long startDelay;
+                        long duration;
+                        if (changeReason == APPEARING) {
+                            startDelay = mChangingAppearingDelay + staggerDelay;
+                            staggerDelay += mChangingAppearingStagger;
+                            duration = mChangingAppearingDuration;
+                        } else {
+                            startDelay = mChangingDisappearingDelay + staggerDelay;
+                            staggerDelay += mChangingDisappearingStagger;
+                            duration = mChangingDisappearingDuration;
+                        }
+                        anim.setStartDelay(startDelay);
+                        anim.setDuration(duration);
+
+                        // Remove the animation from the cache when it ends
+                        anim.addListener(new AnimatorListenerAdapter() {
+                            private boolean canceled = false;
+                            public void onAnimationCancel(Animator animator) {
+                                // we remove canceled animations immediately, not here
+                                canceled = true;
+                            }
+                            public void onAnimationEnd(Animator animator) {
+                                if (!canceled) {
+                                    currentAnimations.remove(child);
+                                }
+                            }
+                        });
+                        if (anim instanceof ObjectAnimator) {
+                            ((ObjectAnimator) anim).setCurrentPlayTime(0);
+                        }
+                        anim.start();
+
+                        // this only removes listeners whose views changed - must clear the
+                        // other listeners later
+                        child.removeOnLayoutChangeListener(this);
+                        layoutChangeListenerMap.remove(child);
+                    }
+                };
+                child.addOnLayoutChangeListener(listener);
+                // cache the listener for later removal
+                layoutChangeListenerMap.put(child, listener);
+            }
+        }
+        // This is the cleanup step. When we get this rendering event, we know that all of
+        // the appropriate animations have been set up and run. Now we can clear out the
+        // layout listeners.
+        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+            public boolean onPreDraw() {
+                observer.removeOnPreDrawListener(this);
+                int numChildren = parent.getChildCount();
+                for (int i = 0; i < numChildren; ++i) {
+                    final View child = parent.getChildAt(i);
+                    child.removeOnLayoutChangeListener(layoutChangeListenerMap.get(child));
+                }
+                layoutChangeListenerMap.clear();
+                return true;
+            }
+        });
+    }
+
+    /**
+     * This method runs the animation that makes an added item appear.
+     *
+     * @param parent The ViewGroup to which the View is being added.
+     * @param child The View being added to the ViewGroup.
+     */
+    private void runAppearingTransition(final ViewGroup parent, final View child) {
+        Animator anim = mAppearingAnim.clone();
+        anim.setTarget(child);
+        anim.setStartDelay(mAppearingDelay);
+        anim.setDuration(mAppearingDuration);
+        if (anim instanceof ObjectAnimator) {
+            ((ObjectAnimator) anim).setCurrentPlayTime(0);
+        }
+        if (mListeners != null) {
+            anim.addListener(new AnimatorListenerAdapter() {
+                public void onAnimationEnd() {
+                    for (TransitionListener listener : mListeners) {
+                        listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
+                    }
+                }
+            });
+        }
+        anim.start();
+    }
+
+    /**
+     * This method runs the animation that makes a removed item disappear.
+     *
+     * @param parent The ViewGroup from which the View is being removed.
+     * @param child The View being removed from the ViewGroup.
+     */
+    private void runDisappearingTransition(final ViewGroup parent, final View child) {
+        Animator anim = mDisappearingAnim.clone();
+        anim.setStartDelay(mDisappearingDelay);
+        anim.setDuration(mDisappearingDuration);
+        anim.setTarget(child);
+        if (mListeners != null) {
+            anim.addListener(new AnimatorListenerAdapter() {
+                public void onAnimationEnd() {
+                    for (TransitionListener listener : mListeners) {
+                        listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
+                    }
+                }
+            });
+        }
+        if (anim instanceof ObjectAnimator) {
+            ((ObjectAnimator) anim).setCurrentPlayTime(0);
+        }
+        anim.start();
+    }
+
+    /**
+     * This method is called by ViewGroup when a child view is about to be added to the
+     * container. This callback starts the process of a transition; we grab the starting
+     * values, listen for changes to all of the children of the container, and start appropriate
+     * animations.
+     *
+     * @param parent The ViewGroup to which the View is being added.
+     * @param child The View being added to the ViewGroup.
+     */
+    public void childAdd(ViewGroup parent, View child) {
+        if (mListeners != null) {
+            for (TransitionListener listener : mListeners) {
+                listener.startTransition(this, parent, child, APPEARING);
+            }
+        }
+        runChangeTransition(parent, child, APPEARING);
+        runAppearingTransition(parent, child);
+    }
+
+    /**
+     * This method is called by ViewGroup when a child view is about to be removed from the
+     * container. This callback starts the process of a transition; we grab the starting
+     * values, listen for changes to all of the children of the container, and start appropriate
+     * animations.
+     *
+     * @param parent The ViewGroup from which the View is being removed.
+     * @param child The View being removed from the ViewGroup.
+     */
+    public void childRemove(ViewGroup parent, View child) {
+        if (mListeners != null) {
+            for (TransitionListener listener : mListeners) {
+                listener.startTransition(this, parent, child, DISAPPEARING);
+            }
+        }
+        runChangeTransition(parent, child, DISAPPEARING);
+        runDisappearingTransition(parent, child);
+    }
+
+    /**
+     * Add a listener that will be called when the bounds of the view change due to
+     * layout processing.
+     *
+     * @param listener The listener that will be called when layout bounds change.
+     */
+    public void addTransitionListener(TransitionListener listener) {
+        if (mListeners == null) {
+            mListeners = new ArrayList<TransitionListener>();
+        }
+        mListeners.add(listener);
+    }
+
+    /**
+     * Remove a listener for layout changes.
+     *
+     * @param listener The listener for layout bounds change.
+     */
+    public void removeTransitionListener(TransitionListener listener) {
+        if (mListeners == null) {
+            return;
+        }
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Gets the current list of listeners for layout changes.
+     * @return
+     */
+    public List<TransitionListener> getTransitionListeners() {
+        return mListeners;
+    }
+
+    /**
+     * This interface is used for listening to starting and ending events for transitions.
+     */
+    public interface TransitionListener {
+
+        /**
+         * This event is sent to listeners when an APPEARING or DISAPPEARING transition
+         * begins.
+         *
+         * @param transition The LayoutTransition sending out the event.
+         * @param container The ViewGroup on which the transition is playing.
+         * @param view The View object being added or removed from its parent.
+         * @param transitionType The type of transition that is beginning, either
+         * {@link android.animation.LayoutTransition#APPEARING} or
+         * {@link android.animation.LayoutTransition#DISAPPEARING}.
+         */
+        public void startTransition(LayoutTransition transition, ViewGroup container,
+                View view, int transitionType);
+
+        /**
+         * This event is sent to listeners when an APPEARING or DISAPPEARING transition ends.
+         *
+         * @param transition The LayoutTransition sending out the event.
+         * @param container The ViewGroup on which the transition is playing.
+         * @param view The View object being added or removed from its parent.
+         * @param transitionType The type of transition that is ending, either
+         * {@link android.animation.LayoutTransition#APPEARING} or
+         * {@link android.animation.LayoutTransition#DISAPPEARING}.
+         */
+        public void endTransition(LayoutTransition transition, ViewGroup container,
+                View view, int transitionType);
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/android/animation/PropertyAnimator.java b/core/java/android/animation/ObjectAnimator.java
similarity index 84%
rename from core/java/android/animation/PropertyAnimator.java
rename to core/java/android/animation/ObjectAnimator.java
index 8a6edcc..6cb90be 100644
--- a/core/java/android/animation/PropertyAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -21,13 +21,13 @@
 import java.lang.reflect.Method;
 
 /**
- * This subclass of {@link Animator} provides support for animating properties on target objects.
+ * This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
  * The constructors of this class take parameters to define the target object that will be animated
  * as well as the name of the property that will be animated. Appropriate set/get functions
  * are then determined internally and the animation will call these functions as necessary to
  * animate the property.
  */
-public final class PropertyAnimator<T> extends Animator<T> {
+public final class ObjectAnimator<T> extends ValueAnimator<T> {
 
     // The target object on which the property exists, set in the constructor
     private Object mTarget;
@@ -47,7 +47,7 @@
      * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
      * the setter function will fail.</p>
      *
-     * <p>If this PropertyAnimator has been set up to animate several properties together,
+     * <p>If this ObjectAnimator has been set up to animate several properties together,
      * using more than one PropertyValuesHolder objects, then setting the propertyName simply
      * sets the propertyName in the first of those PropertyValuesHolder objects.</p>
      *
@@ -101,18 +101,18 @@
         try {
             returnVal = mTarget.getClass().getMethod(setterName, args);
         } catch (NoSuchMethodException e) {
-            Log.e("PropertyAnimator",
+            Log.e("ObjectAnimator",
                     "Couldn't find setter/getter for property " + mPropertyName + ": " + e);
         }
         return returnVal;
     }
 
     /**
-     * Creates a new PropertyAnimator object. This default constructor is primarily for
+     * Creates a new ObjectAnimator object. This default constructor is primarily for
      * use internally; the other constructors which take parameters are more generally
      * useful.
      */
-    public PropertyAnimator() {
+    public ObjectAnimator() {
     }
 
     /**
@@ -128,7 +128,7 @@
      * is assumed to be the final value being animated to, and the initial value will be
      * derived on the fly.
      */
-    public PropertyAnimator(long duration, Object target, String propertyName, T...values) {
+    public ObjectAnimator(long duration, Object target, String propertyName, T...values) {
         super(duration, (T[]) values);
         mTarget = target;
         setPropertyName(propertyName);
@@ -136,7 +136,7 @@
 
     /**
      * A constructor that takes <code>PropertyValueHolder</code> values. This constructor should
-     * be used when animating several properties at once with the same PropertyAnimator, since
+     * be used when animating several properties at once with the same ObjectAnimator, since
      * PropertyValuesHolder allows you to associate a set of animation values with a property
      * name.
      *
@@ -148,7 +148,7 @@
      * @param values The PropertyValuesHolder objects which hold each the property name and values
      * to animate that property between.
      */
-    public PropertyAnimator(long duration, Object target, PropertyValuesHolder...values) {
+    public ObjectAnimator(long duration, Object target, PropertyValuesHolder...values) {
         super(duration);
         setValues(values);
         mTarget = target;
@@ -168,10 +168,14 @@
      */
     @Override
     void initAnimation() {
-        super.initAnimation();
-        int numValues = mValues.length;
-        for (int i = 0; i < numValues; ++i) {
-            mValues[i].setupSetterAndGetter(mTarget);
+        if (!mInitialized) {
+            // mValueType may change due to setter/getter setup; do this before calling super.init(),
+            // which uses mValueType to set up the default type evaluator.
+            int numValues = mValues.length;
+            for (int i = 0; i < numValues; ++i) {
+                mValues[i].setupSetterAndGetter(mTarget);
+            }
+            super.initAnimation();
         }
     }
 
@@ -190,10 +194,29 @@
      *
      * @param target The object being animated
      */
+    @Override
     public void setTarget(Object target) {
         mTarget = target;
     }
 
+    @Override
+    public void setupStartValues() {
+        initAnimation();
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].setupStartValue(mTarget);
+        }
+    }
+
+    @Override
+    public void setupEndValues() {
+        initAnimation();
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].setupEndValue(mTarget);
+        }
+    }
+
     /**
      * This method is called with the elapsed fraction of the animation during every
      * animation frame. This function turns the elapsed fraction into an interpolated fraction
@@ -216,8 +239,8 @@
     }
 
     @Override
-    public PropertyAnimator clone() throws CloneNotSupportedException {
-        final PropertyAnimator anim = (PropertyAnimator) super.clone();
+    public ObjectAnimator clone() {
+        final ObjectAnimator anim = (ObjectAnimator) super.clone();
         return anim;
     }
 }
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index fc829b8..1d46123 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -25,29 +25,33 @@
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
+ * This class holds information about a property and the values that that property
+ * should take on during an animation. PropertyValuesHolder objects can be used to create
+ * animations with ValueAnimator or ObjectAnimator that operate on several different properties
+ * in parallel.
  */
-public class PropertyValuesHolder<T> {
+public class PropertyValuesHolder<T> implements Cloneable {
 
     /**
      * The name of the property associated with the values. This need not be a real property,
-     * unless this object is being used with PropertyAnimator. But this is the name by which
-     * aniamted values are looked up with getAnimatedValue(String) in Animator.
+     * unless this object is being used with ObjectAnimator. But this is the name by which
+     * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
      */
     private String mPropertyName;
 
     /**
-     * The setter function, if needed. PropertyAnimator hands off this functionality to
+     * The setter function, if needed. ObjectAnimator hands off this functionality to
      * PropertyValuesHolder, since it holds all of the per-property information. This
      * property can be manually set via setSetter(). Otherwise, it is automatically
-     * derived when the animation starts in setupSetterAndGetter() if using PropertyAnimator.
+     * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
      */
     private Method mSetter = null;
 
     /**
-     * The getter function, if needed. PropertyAnimator hands off this functionality to
+     * The getter function, if needed. ObjectAnimator hands off this functionality to
      * PropertyValuesHolder, since it holds all of the per-property information. This
      * property can be manually set via setSetter(). Otherwise, it is automatically
-     * derived when the animation starts in setupSetterAndGetter() if using PropertyAnimator.
+     * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
      * The getter is only derived and used if one of the values is null.
      */
     private Method mGetter = null;
@@ -108,16 +112,16 @@
 
     /**
      * The value most recently calculated by calculateValue(). This is set during
-     * that function and might be retrieved later either by Animator.animatedValue() or
-     * by the property-setting logic in PropertyAnimator.animatedValue().
+     * that function and might be retrieved later either by ValueAnimator.animatedValue() or
+     * by the property-setting logic in ObjectAnimator.animatedValue().
      */
     private Object mAnimatedValue;
 
     /**
      * Constructs a PropertyValuesHolder object with just a set of values. This constructor
-     * is typically not used when animating objects with PropertyAnimator, because that
+     * is typically not used when animating objects with ObjectAnimator, because that
      * object needs distinct and meaningful property names. Simpler animations of one
-     * set of values using Animator may use this constructor, however, because no
+     * set of values using ValueAnimator may use this constructor, however, because no
      * distinguishing name is needed.
      * @param values The set of values to animate between. If there is only one value, it
      * is assumed to be the final value being animated to, and the initial value will be
@@ -137,14 +141,14 @@
      * on the object. Also, if any value is null, the value will be filled in when the animation
      * starts in the same way. This mechanism of automatically getting null values only works
      * if the PropertyValuesHolder object is used in conjunction
-     * {@link android.animation.PropertyAnimator}, and with a getter function either
+     * {@link ObjectAnimator}, and with a getter function either
      * derived automatically from <code>propertyName</code> or set explicitly via
      * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
      * no way of determining what the value should be.
      * @param propertyName The name of the property associated with this set of values. This
-     * can be the actual property name to be used when using a PropertyAnimator object, or
+     * can be the actual property name to be used when using a ObjectAnimator object, or
      * just a name used to get animated values, such as if this object is used with an
-     * Animator object.
+     * ValueAnimator object.
      * @param values The set of values to animate between.
      */
     public PropertyValuesHolder(String propertyName, T... values) {
@@ -159,7 +163,7 @@
      * on the object. Also, if any value is null, the value will be filled in when the animation
      * starts in the same way. This mechanism of automatically getting null values only works
      * if the PropertyValuesHolder object is used in conjunction
-     * {@link android.animation.PropertyAnimator}, and with a getter function either
+     * {@link ObjectAnimator}, and with a getter function either
      * derived automatically from <code>propertyName</code> or set explicitly via
      * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
      * no way of determining what the value should be.
@@ -192,7 +196,7 @@
             }
         } else {
             if (numKeyframes == 1) {
-                keyframes[0] = new Keyframe(0f, null);
+                keyframes[0] = new Keyframe(0f, (Object) null);
                 keyframes[1] = new Keyframe(1f, values[0]);
             } else {
                 keyframes[0] = new Keyframe(0f, values[0]);
@@ -256,6 +260,8 @@
                 args[0] = typeVariant;
                 try {
                     returnVal = targetClass.getMethod(methodName, args);
+                    // change the value type to suit
+                    mValueType = typeVariant;
                     return returnVal;
                 } catch (NoSuchMethodException e) {
                     // Swallow the error and keep trying other variants
@@ -325,7 +331,7 @@
     }
 
     /**
-     * Internal function (called from PropertyAnimator) to set up the setter and getter
+     * Internal function (called from ObjectAnimator) to set up the setter and getter
      * prior to running the animation. If the setter has not been manually set for this
      * object, it will be derived automatically given the property name, target object, and
      * types of values supplied. If no getter has been set, it will be supplied iff any of the
@@ -356,9 +362,66 @@
     }
 
     /**
+     * Utility function to set the value stored in a particular Keyframe. The value used is
+     * whatever the value is for the property name specified in the keyframe on the target object.
+     *
+     * @param target The target object from which the current value should be extracted.
+     * @param kf The keyframe which holds the property name and value.
+     */
+    private void setupValue(Object target, Keyframe kf) {
+        try {
+            if (mGetter == null) {
+                Class targetClass = target.getClass();
+                setupGetter(targetClass);
+            }
+            kf.setValue((T) mGetter.invoke(target));
+        } catch (InvocationTargetException e) {
+            Log.e("PropertyValuesHolder", e.toString());
+        } catch (IllegalAccessException e) {
+            Log.e("PropertyValuesHolder", e.toString());
+        }
+    }
+
+    /**
+     * This function is called by ObjectAnimator when setting the start values for an animation.
+     * The start values are set according to the current values in the target object. The
+     * property whose value is extracted is whatever is specified by the propertyName of this
+     * PropertyValuesHolder object.
+     *
+     * @param target The object which holds the start values that should be set.
+     */
+    void setupStartValue(Object target) {
+        setupValue(target, mKeyframeSet.mKeyframes.get(0));
+    }
+
+    /**
+     * This function is called by ObjectAnimator when setting the end values for an animation.
+     * The end values are set according to the current values in the target object. The
+     * property whose value is extracted is whatever is specified by the propertyName of this
+     * PropertyValuesHolder object.
+     *
+     * @param target The object which holds the start values that should be set.
+     */
+    void setupEndValue(Object target) {
+        setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
+    }
+
+    @Override
+    public PropertyValuesHolder clone() {
+        ArrayList<Keyframe> keyframes = mKeyframeSet.mKeyframes;
+        int numKeyframes = mKeyframeSet.mKeyframes.size();
+        Keyframe[] newKeyframes = new Keyframe[numKeyframes];
+        for (int i = 0; i < numKeyframes; ++i) {
+            newKeyframes[i] = keyframes.get(i).clone();
+        }
+        PropertyValuesHolder pvhClone = new PropertyValuesHolder(mPropertyName,
+                (Object[]) newKeyframes);
+        return pvhClone;
+    }
+    /**
      * Internal function to set the value on the target object, using the setter set up
-     * earlier on this PropertyValuesHolder object. This function is called by PropertyAnimator
-     * to handle turning the value calculated by Animator into a value set on the object
+     * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+     * to handle turning the value calculated by ValueAnimator into a value set on the object
      * according to the name of the property.
      * @param target The target object on which the value is set
      */
@@ -376,13 +439,14 @@
     }
 
     /**
-     * Internal function, called by Animator, to set up the TypeEvaluator that will be used
+     * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
      * to calculate animated values.
      */
     void init() {
         if (mEvaluator == null) {
-            mEvaluator = (mValueType == int.class) ? sIntEvaluator :
-                (mValueType == double.class) ? sDoubleEvaluator : sFloatEvaluator;
+            mEvaluator = (mValueType == int.class || mValueType == Integer.class) ? sIntEvaluator :
+                (mValueType == double.class || mValueType == Double.class) ? sDoubleEvaluator :
+                        sFloatEvaluator;
         }
     }
 
@@ -402,7 +466,7 @@
 
     /**
      * Function used to calculate the value according to the evaluator set up for
-     * this PropertyValuesHolder object. This function is called by Animator.animateValue().
+     * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
      *
      * @param fraction The elapsed, interpolated fraction of the animation.
      * @return The calculated value at this point in the animation.
@@ -419,7 +483,7 @@
      * approach is more direct, and is especially useful when a function must be called that does
      * not correspond to the convention of <code>setName()</code>. For example, if a function
      * called <code>offset()</code> is to be called with the animated values, there is no way
-     * to tell <code>PropertyAnimator</code> how to call that function simply through a property
+     * to tell <code>ObjectAnimator</code> how to call that function simply through a property
      * name, so a setter method should be supplied instead.
      *
      * <p>Note that the setter function must take the same parameter type as the
@@ -447,7 +511,7 @@
      * approach is more direct, and is especially useful when a function must be called that does
      * not correspond to the convention of <code>setName()</code>. For example, if a function
      * called <code>offset()</code> is to be called to get an initial value, there is no way
-     * to tell <code>PropertyAnimator</code> how to call that function simply through a property
+     * to tell <code>ObjectAnimator</code> how to call that function simply through a property
      * name, so a getter method should be supplied instead.
      *
      * <p>Note that the getter method is only called whether supplied here or derived
@@ -505,7 +569,7 @@
     }
 
     /**
-     * Internal function, called by Animator and PropertyAnimator, to retrieve the value
+     * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
      * most recently calculated in calculateValue().
      * @return
      */
diff --git a/core/java/android/animation/TypeEvaluator.java b/core/java/android/animation/TypeEvaluator.java
index 6150e00..fa49175 100644
--- a/core/java/android/animation/TypeEvaluator.java
+++ b/core/java/android/animation/TypeEvaluator.java
@@ -17,12 +17,12 @@
 package android.animation;
 
 /**
- * Interface for use with the {@link Animator#setEvaluator(TypeEvaluator)} function. Evaluators
+ * Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
  * allow developers to create animations on arbitrary property types, by allowing them to supply
  * custom evaulators for types that are not automatically understood and used by the animation
  * system.
  *
- * @see Animator#setEvaluator(TypeEvaluator)
+ * @see ValueAnimator#setEvaluator(TypeEvaluator)
  */
 public interface TypeEvaluator {
 
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
new file mode 100755
index 0000000..54a8e4b
--- /dev/null
+++ b/core/java/android/animation/ValueAnimator.java
@@ -0,0 +1,972 @@
+/*
+ * 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.animation;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class provides a simple timing engine for running animations
+ * which calculate animated values and set them on target objects.
+ *
+ * <p>There is a single timing pulse that all animations use. It runs in a
+ * custom handler to ensure that property changes happen on the UI thread.</p>
+ *
+ * <p>By default, ValueAnimator uses non-linear time interpolation, via the
+ * {@link AccelerateDecelerateInterpolator} class, which accelerates into and decelerates
+ * out of an animation. This behavior can be changed by calling
+ * {@link ValueAnimator#setInterpolator(Interpolator)}.</p>
+ */
+public class ValueAnimator<T> extends Animator {
+
+    /**
+     * Internal constants
+     */
+
+    /*
+     * The default amount of time in ms between animation frames
+     */
+    private static final long DEFAULT_FRAME_DELAY = 30;
+
+    /**
+     * Messages sent to timing handler: START is sent when an animation first begins, FRAME is sent
+     * by the handler to itself to process the next animation frame
+     */
+    private static final int ANIMATION_START = 0;
+    private static final int ANIMATION_FRAME = 1;
+
+    /**
+     * Values used with internal variable mPlayingState to indicate the current state of an
+     * animation.
+     */
+    private static final int STOPPED    = 0; // Not yet playing
+    private static final int RUNNING    = 1; // Playing normally
+    private static final int CANCELED   = 2; // cancel() called - need to end it
+    private static final int ENDED      = 3; // end() called - need to end it
+    private static final int SEEKED     = 4; // Seeked to some time value
+
+    /**
+     * Internal variables
+     * NOTE: This object implements the clone() method, making a deep copy of any referenced
+     * objects. As other non-trivial fields are added to this class, make sure to add logic
+     * to clone() to make deep copies of them.
+     */
+
+    // The first time that the animation's animateFrame() method is called. This time is used to
+    // determine elapsed time (and therefore the elapsed fraction) in subsequent calls
+    // to animateFrame()
+    private long mStartTime;
+
+    /**
+     * Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
+     * to a value.
+     */
+    private long mSeekTime = -1;
+
+    // The static sAnimationHandler processes the internal timing loop on which all animations
+    // are based
+    private static AnimationHandler sAnimationHandler;
+
+    // The static list of all active animations
+    private static final ArrayList<ValueAnimator> sAnimations = new ArrayList<ValueAnimator>();
+
+    // The set of animations to be started on the next animation frame
+    private static final ArrayList<ValueAnimator> sPendingAnimations = new ArrayList<ValueAnimator>();
+
+    // The time interpolator to be used if none is set on the animation
+    private static final Interpolator sDefaultInterpolator = new AccelerateDecelerateInterpolator();
+
+    // type evaluators for the three primitive types handled by this implementation
+    private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
+    private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
+    private static final TypeEvaluator sDoubleEvaluator = new DoubleEvaluator();
+
+    /**
+     * Used to indicate whether the animation is currently playing in reverse. This causes the
+     * elapsed fraction to be inverted to calculate the appropriate values.
+     */
+    private boolean mPlayingBackwards = false;
+
+    /**
+     * This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the
+     * repeatCount (if repeatCount!=INFINITE), the animation ends
+     */
+    private int mCurrentIteration = 0;
+
+    /**
+     * Tracks whether a startDelay'd animation has begun playing through the startDelay.
+     */
+    private boolean mStartedDelay = false;
+
+    /**
+     * Tracks the time at which the animation began playing through its startDelay. This is
+     * different from the mStartTime variable, which is used to track when the animation became
+     * active (which is when the startDelay expired and the animation was added to the active
+     * animations list).
+     */
+    private long mDelayStartTime;
+
+    /**
+     * Flag that represents the current state of the animation. Used to figure out when to start
+     * an animation (if state == STOPPED). Also used to end an animation that
+     * has been cancel()'d or end()'d since the last animation frame. Possible values are
+     * STOPPED, RUNNING, ENDED, CANCELED.
+     */
+    private int mPlayingState = STOPPED;
+
+    /**
+     * Internal collections used to avoid set collisions as animations start and end while being
+     * processed.
+     */
+    private static final ArrayList<ValueAnimator> sEndingAnims = new ArrayList<ValueAnimator>();
+    private static final ArrayList<ValueAnimator> sDelayedAnims = new ArrayList<ValueAnimator>();
+    private static final ArrayList<ValueAnimator> sReadyAnims = new ArrayList<ValueAnimator>();
+
+    /**
+     * Flag that denotes whether the animation is set up and ready to go. Used to
+     * set up animation that has not yet been started.
+     */
+    boolean mInitialized = false;
+
+    //
+    // Backing variables
+    //
+
+    // How long the animation should last in ms
+    private long mDuration;
+
+    // The amount of time in ms to delay starting the animation after start() is called
+    private long mStartDelay = 0;
+
+    // The number of milliseconds between animation frames
+    private static long sFrameDelay = DEFAULT_FRAME_DELAY;
+
+    // The number of times the animation will repeat. The default is 0, which means the animation
+    // will play only once
+    private int mRepeatCount = 0;
+
+    /**
+     * The type of repetition that will occur when repeatMode is nonzero. RESTART means the
+     * animation will start from the beginning on every new cycle. REVERSE means the animation
+     * will reverse directions on each iteration.
+     */
+    private int mRepeatMode = RESTART;
+
+    /**
+     * The time interpolator to be used. The elapsed fraction of the animation will be passed
+     * through this interpolator to calculate the interpolated fraction, which is then used to
+     * calculate the animated values.
+     */
+    private Interpolator mInterpolator = sDefaultInterpolator;
+
+    /**
+     * The set of listeners to be sent events through the life of an animation.
+     */
+    private ArrayList<AnimatorUpdateListener> mUpdateListeners = null;
+
+    /**
+     * The property/value sets being animated.
+     */
+    PropertyValuesHolder[] mValues;
+
+    /**
+     * A hashmap of the PropertyValuesHolder objects. This map is used to lookup animated values
+     * by property name during calls to getAnimatedValue(String).
+     */
+    HashMap<String, PropertyValuesHolder> mValuesMap;
+
+    /**
+     * Public constants
+     */
+
+    /**
+     * When the animation reaches the end and <code>repeatCount</code> is INFINITE
+     * or a positive value, the animation restarts from the beginning.
+     */
+    public static final int RESTART = 1;
+    /**
+     * When the animation reaches the end and <code>repeatCount</code> is INFINITE
+     * or a positive value, the animation reverses direction on every iteration.
+     */
+    public static final int REVERSE = 2;
+    /**
+     * This value used used with the {@link #setRepeatCount(int)} property to repeat
+     * the animation indefinitely.
+     */
+    public static final int INFINITE = -1;
+
+    /**
+     * Creates a new ValueAnimator object. This default constructor is primarily for
+     * use internally; the other constructors which take parameters are more generally
+     * useful.
+     */
+    public ValueAnimator() {
+    }
+
+    /**
+     * Constructs an ValueAnimator object with the specified duration and set of
+     * values. If the values are a set of PropertyValuesHolder objects, then these objects
+     * define the potentially multiple properties being animated and the values the properties are
+     * animated between. Otherwise, the values define a single set of values animated between.
+     *
+     * @param duration The length of the animation, in milliseconds.
+     * @param values The set of values to animate between. If these values are not
+     * PropertyValuesHolder objects, then there should be more than one value, since the values
+     * determine the interval to animate between.
+     */
+    public ValueAnimator(long duration, T...values) {
+        mDuration = duration;
+        if (values.length > 0) {
+            setValues(values);
+        }
+    }
+
+    /**
+     * Sets the values, per property, being animated between. This function is called internally
+     * by the constructors of ValueAnimator that take a list of values. But an ValueAnimator can
+     * be constructed without values and this method can be called to set the values manually
+     * instead.
+     *
+     * @param values The set of values, per property, being animated between.
+     */
+    public void setValues(PropertyValuesHolder... values) {
+        int numValues = values.length;
+        mValues = values;
+        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
+        for (int i = 0; i < numValues; ++i) {
+            PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
+            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
+        }
+    }
+
+    /**
+     * Returns the values that this ValueAnimator animates between. These values are stored in
+     * PropertyValuesHolder objects, even if the ValueAnimator was created with a simple list
+     * of value objects instead.
+     *
+     * @return PropertyValuesHolder[] An array of PropertyValuesHolder objects which hold the
+     * values, per property, that define the animation.
+     */
+    public PropertyValuesHolder[] getValues() {
+        return mValues;
+    }
+
+    /**
+     * Sets the values to animate between for this animation. If <code>values</code> is
+     * a set of PropertyValuesHolder objects, these objects will become the set of properties
+     * animated and the values that those properties are animated between. Otherwise, this method
+     * will set only one set of values for the ValueAnimator. Also, if the values are not
+     * PropertyValuesHolder objects and if there are already multiple sets of
+     * values defined for this ValueAnimator via
+     * more than one PropertyValuesHolder objects, this method will set the values for
+     * the first of those objects.
+     *
+     * @param values The set of values to animate between.
+     */
+    public void setValues(T... values) {
+        if (mValues == null || mValues.length == 0) {
+            setValues(new PropertyValuesHolder[]{
+                    new PropertyValuesHolder("", (Object[])values)});
+        } else {
+            PropertyValuesHolder valuesHolder = mValues[0];
+            valuesHolder.setValues(values);
+        }
+    }
+
+    /**
+     * This function is called immediately before processing the first animation
+     * frame of an animation. If there is a nonzero <code>startDelay</code>, the
+     * function is called after that delay ends.
+     * It takes care of the final initialization steps for the
+     * animation.
+     *
+     *  <p>Overrides of this method should call the superclass method to ensure
+     *  that internal mechanisms for the animation are set up correctly.</p>
+     */
+    void initAnimation() {
+        if (!mInitialized) {
+            int numValues = mValues.length;
+            for (int i = 0; i < numValues; ++i) {
+                mValues[i].init();
+            }
+            mCurrentIteration = 0;
+            mInitialized = true;
+        }
+    }
+
+
+    /**
+     * Sets the length of the animation.
+     *
+     * @param duration The length of the animation, in milliseconds.
+     */
+    public void setDuration(long duration) {
+        mDuration = duration;
+    }
+
+    /**
+     * Gets the length of the animation.
+     *
+     * @return The length of the animation, in milliseconds.
+     */
+    public long getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Sets the position of the animation to the specified point in time. This time should
+     * be between 0 and the total duration of the animation, including any repetition. If
+     * the animation has not yet been started, then it will not advance forward after it is
+     * set to this time; it will simply set the time to this value and perform any appropriate
+     * actions based on that time. If the animation is already running, then setCurrentPlayTime()
+     * will set the current playing time to this value and continue playing from that point.
+     *
+     * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
+     */
+    public void setCurrentPlayTime(long playTime) {
+        initAnimation();
+        long currentTime = AnimationUtils.currentAnimationTimeMillis();
+        if (mPlayingState != RUNNING) {
+            mSeekTime = playTime;
+            mPlayingState = SEEKED;
+        }
+        mStartTime = currentTime - playTime;
+        animationFrame(currentTime);
+    }
+
+    /**
+     * Gets the current position of the animation in time, which is equal to the current
+     * time minus the time that the animation started. An animation that is not yet started will
+     * return a value of zero.
+     *
+     * @return The current position in time of the animation.
+     */
+    public long getCurrentPlayTime() {
+        if (!mInitialized || mPlayingState == STOPPED) {
+            return 0;
+        }
+        return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
+    }
+
+    /**
+     * This custom, static handler handles the timing pulse that is shared by
+     * all active animations. This approach ensures that the setting of animation
+     * values will happen on the UI thread and that all animations will share
+     * the same times for calculating their values, which makes synchronizing
+     * animations possible.
+     *
+     */
+    private static class AnimationHandler extends Handler {
+        /**
+         * There are only two messages that we care about: ANIMATION_START and
+         * ANIMATION_FRAME. The START message is sent when an animation's start()
+         * method is called. It cannot start synchronously when start() is called
+         * because the call may be on the wrong thread, and it would also not be
+         * synchronized with other animations because it would not start on a common
+         * timing pulse. So each animation sends a START message to the handler, which
+         * causes the handler to place the animation on the active animations queue and
+         * start processing frames for that animation.
+         * The FRAME message is the one that is sent over and over while there are any
+         * active animations to process.
+         */
+        @Override
+        public void handleMessage(Message msg) {
+            boolean callAgain = true;
+            switch (msg.what) {
+                // TODO: should we avoid sending frame message when starting if we
+                // were already running?
+                case ANIMATION_START:
+                    if (sAnimations.size() > 0 || sDelayedAnims.size() > 0) {
+                        callAgain = false;
+                    }
+                    // pendingAnims holds any animations that have requested to be started
+                    // We're going to clear sPendingAnimations, but starting animation may
+                    // cause more to be added to the pending list (for example, if one animation
+                    // starting triggers another starting). So we loop until sPendingAnimations
+                    // is empty.
+                    while (sPendingAnimations.size() > 0) {
+                        ArrayList<ValueAnimator> pendingCopy =
+                                (ArrayList<ValueAnimator>) sPendingAnimations.clone();
+                        sPendingAnimations.clear();
+                        int count = pendingCopy.size();
+                        for (int i = 0; i < count; ++i) {
+                            ValueAnimator anim = pendingCopy.get(i);
+                            // If the animation has a startDelay, place it on the delayed list
+                            if (anim.mStartDelay == 0 || anim.mPlayingState == ENDED ||
+                                    anim.mPlayingState == CANCELED) {
+                                anim.startAnimation();
+                            } else {
+                                sDelayedAnims.add(anim);
+                            }
+                        }
+                    }
+                    // fall through to process first frame of new animations
+                case ANIMATION_FRAME:
+                    // currentTime holds the common time for all animations processed
+                    // during this frame
+                    long currentTime = AnimationUtils.currentAnimationTimeMillis();
+
+                    // First, process animations currently sitting on the delayed queue, adding
+                    // them to the active animations if they are ready
+                    int numDelayedAnims = sDelayedAnims.size();
+                    for (int i = 0; i < numDelayedAnims; ++i) {
+                        ValueAnimator anim = sDelayedAnims.get(i);
+                        if (anim.delayedAnimationFrame(currentTime)) {
+                            sReadyAnims.add(anim);
+                        }
+                    }
+                    int numReadyAnims = sReadyAnims.size();
+                    if (numReadyAnims > 0) {
+                        for (int i = 0; i < numReadyAnims; ++i) {
+                            ValueAnimator anim = sReadyAnims.get(i);
+                            anim.startAnimation();
+                            sDelayedAnims.remove(anim);
+                        }
+                        sReadyAnims.clear();
+                    }
+
+                    // Now process all active animations. The return value from animationFrame()
+                    // tells the handler whether it should now be ended
+                    int numAnims = sAnimations.size();
+                    for (int i = 0; i < numAnims; ++i) {
+                        ValueAnimator anim = sAnimations.get(i);
+                        if (anim.animationFrame(currentTime)) {
+                            sEndingAnims.add(anim);
+                        }
+                    }
+                    if (sEndingAnims.size() > 0) {
+                        for (int i = 0; i < sEndingAnims.size(); ++i) {
+                            sEndingAnims.get(i).endAnimation();
+                        }
+                        sEndingAnims.clear();
+                    }
+
+                    // If there are still active or delayed animations, call the handler again
+                    // after the frameDelay
+                    if (callAgain && (!sAnimations.isEmpty() || !sDelayedAnims.isEmpty())) {
+                        sendEmptyMessageDelayed(ANIMATION_FRAME, sFrameDelay);
+                    }
+                    break;
+            }
+        }
+    }
+
+    /**
+     * The amount of time, in milliseconds, to delay starting the animation after
+     * {@link #start()} is called.
+     *
+     * @return the number of milliseconds to delay running the animation
+     */
+    public long getStartDelay() {
+        return mStartDelay;
+    }
+
+    /**
+     * The amount of time, in milliseconds, to delay starting the animation after
+     * {@link #start()} is called.
+
+     * @param startDelay The amount of the delay, in milliseconds
+     */
+    public void setStartDelay(long startDelay) {
+        this.mStartDelay = startDelay;
+    }
+
+    /**
+     * The amount of time, in milliseconds, between each frame of the animation. This is a
+     * requested time that the animation will attempt to honor, but the actual delay between
+     * frames may be different, depending on system load and capabilities. This is a static
+     * function because the same delay will be applied to all animations, since they are all
+     * run off of a single timing loop.
+     *
+     * @return the requested time between frames, in milliseconds
+     */
+    public static long getFrameDelay() {
+        return sFrameDelay;
+    }
+
+    /**
+     * The amount of time, in milliseconds, between each frame of the animation. This is a
+     * requested time that the animation will attempt to honor, but the actual delay between
+     * frames may be different, depending on system load and capabilities. This is a static
+     * function because the same delay will be applied to all animations, since they are all
+     * run off of a single timing loop.
+     *
+     * @param frameDelay the requested time between frames, in milliseconds
+     */
+    public static void setFrameDelay(long frameDelay) {
+        sFrameDelay = frameDelay;
+    }
+
+    /**
+     * The most recent value calculated by this <code>ValueAnimator</code> when there is just one
+     * property being animated. This value is only sensible while the animation is running. The main
+     * purpose for this read-only property is to retrieve the value from the <code>ValueAnimator</code>
+     * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
+     * is called during each animation frame, immediately after the value is calculated.
+     *
+     * @return animatedValue The value most recently calculated by this <code>ValueAnimator</code> for
+     * the single property being animated. If there are several properties being animated
+     * (specified by several PropertyValuesHolder objects in the constructor), this function
+     * returns the animated value for the first of those objects.
+     */
+    public Object getAnimatedValue() {
+        if (mValues != null && mValues.length > 0) {
+            return mValues[0].getAnimatedValue();
+        }
+        // Shouldn't get here; should always have values unless ValueAnimator was set up wrong
+        return null;
+    }
+
+    /**
+     * The most recent value calculated by this <code>ValueAnimator</code> for <code>propertyName</code>.
+     * The main purpose for this read-only property is to retrieve the value from the
+     * <code>ValueAnimator</code> during a call to
+     * {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
+     * is called during each animation frame, immediately after the value is calculated.
+     *
+     * @return animatedValue The value most recently calculated for the named property
+     * by this <code>ValueAnimator</code>.
+     */
+    public Object getAnimatedValue(String propertyName) {
+        PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName);
+        if (valuesHolder != null) {
+            return valuesHolder.getAnimatedValue();
+        } else {
+            // At least avoid crashing if called with bogus propertyName
+            return null;
+        }
+    }
+
+    /**
+     * Sets how many times the animation should be repeated. If the repeat
+     * count is 0, the animation is never repeated. If the repeat count is
+     * greater than 0 or {@link #INFINITE}, the repeat mode will be taken
+     * into account. The repeat count is 0 by default.
+     *
+     * @param value the number of times the animation should be repeated
+     */
+    public void setRepeatCount(int value) {
+        mRepeatCount = value;
+    }
+    /**
+     * Defines how many times the animation should repeat. The default value
+     * is 0.
+     *
+     * @return the number of times the animation should repeat, or {@link #INFINITE}
+     */
+    public int getRepeatCount() {
+        return mRepeatCount;
+    }
+
+    /**
+     * Defines what this animation should do when it reaches the end. This
+     * setting is applied only when the repeat count is either greater than
+     * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
+     *
+     * @param value {@link #RESTART} or {@link #REVERSE}
+     */
+    public void setRepeatMode(int value) {
+        mRepeatMode = value;
+    }
+
+    /**
+     * Defines what this animation should do when it reaches the end.
+     *
+     * @return either one of {@link #REVERSE} or {@link #RESTART}
+     */
+    public int getRepeatMode() {
+        return mRepeatMode;
+    }
+
+    /**
+     * Adds a listener to the set of listeners that are sent update events through the life of
+     * an animation. This method is called on all listeners for every frame of the animation,
+     * after the values for the animation have been calculated.
+     *
+     * @param listener the listener to be added to the current set of listeners for this animation.
+     */
+    public void addUpdateListener(AnimatorUpdateListener listener) {
+        if (mUpdateListeners == null) {
+            mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
+        }
+        mUpdateListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener from the set listening to frame updates for this animation.
+     *
+     * @param listener the listener to be removed from the current set of update listeners
+     * for this animation.
+     */
+    public void removeUpdateListener(AnimatorUpdateListener listener) {
+        if (mUpdateListeners == null) {
+            return;
+        }
+        mUpdateListeners.remove(listener);
+        if (mUpdateListeners.size() == 0) {
+            mUpdateListeners = null;
+        }
+    }
+
+
+    /**
+     * The time interpolator used in calculating the elapsed fraction of this animation. The
+     * interpolator determines whether the animation runs with linear or non-linear motion,
+     * such as acceleration and deceleration. The default value is
+     * {@link android.view.animation.AccelerateDecelerateInterpolator}
+     *
+     * @param value the interpolator to be used by this animation
+     */
+    @Override
+    public void setInterpolator(Interpolator value) {
+        if (value != null) {
+            mInterpolator = value;
+        }
+    }
+
+    /**
+     * Returns the timing interpolator that this ValueAnimator uses.
+     *
+     * @return The timing interpolator for this ValueAnimator.
+     */
+    public Interpolator getInterpolator() {
+        return mInterpolator;
+    }
+
+    /**
+     * The type evaluator to be used when calculating the animated values of this animation.
+     * The system will automatically assign a float, int, or double evaluator based on the type
+     * of <code>startValue</code> and <code>endValue</code> in the constructor. But if these values
+     * are not one of these primitive types, or if different evaluation is desired (such as is
+     * necessary with int values that represent colors), a custom evaluator needs to be assigned.
+     * For example, when running an animation on color values, the {@link RGBEvaluator}
+     * should be used to get correct RGB color interpolation.
+     *
+     * <p>If this ValueAnimator has only one set of values being animated between, this evaluator
+     * will be used for that set. If there are several sets of values being animated, which is
+     * the case if PropertyValuesHOlder objects were set on the ValueAnimator, then the evaluator
+     * is assigned just to the first PropertyValuesHolder object.</p>
+     *
+     * @param value the evaluator to be used this animation
+     */
+    public void setEvaluator(TypeEvaluator value) {
+        if (value != null && mValues != null && mValues.length > 0) {
+            mValues[0].setEvaluator(value);
+        }
+    }
+
+    /**
+     * 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/
+     *
+     * @param playBackwards Whether the ValueAnimator should start playing in reverse.
+     */
+    private void start(boolean playBackwards) {
+        mPlayingBackwards = playBackwards;
+        if ((mStartDelay == 0) && (Thread.currentThread() == Looper.getMainLooper().getThread())) {
+            // This sets the initial value of the animation, prior to actually starting it running
+            setCurrentPlayTime(getCurrentPlayTime());
+        }
+        mPlayingState = STOPPED;
+        mStartedDelay = false;
+        sPendingAnimations.add(this);
+        if (sAnimationHandler == null) {
+            sAnimationHandler = new AnimationHandler();
+        }
+        // TODO: does this put too many messages on the queue if the handler
+        // is already running?
+        sAnimationHandler.sendEmptyMessage(ANIMATION_START);
+    }
+
+    @Override
+    public void start() {
+        start(false);
+    }
+
+    @Override
+    public void cancel() {
+        if (mListeners != null) {
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            for (AnimatorListener listener : tmpListeners) {
+                listener.onAnimationCancel(this);
+            }
+        }
+        // Just set the CANCELED flag - this causes the animation to end the next time a frame
+        // is processed.
+        mPlayingState = CANCELED;
+    }
+
+    @Override
+    public void end() {
+        if (!sAnimations.contains(this) && !sPendingAnimations.contains(this)) {
+            // Special case if the animation has not yet started; get it ready for ending
+            mStartedDelay = false;
+            sPendingAnimations.add(this);
+            if (sAnimationHandler == null) {
+                sAnimationHandler = new AnimationHandler();
+            }
+            sAnimationHandler.sendEmptyMessage(ANIMATION_START);
+        }
+        // Just set the ENDED flag - this causes the animation to end the next time a frame
+        // is processed.
+        mPlayingState = ENDED;
+    }
+
+    @Override
+    public boolean isRunning() {
+        // ENDED or CANCELED indicate that it has been ended or canceled, but not processed yet
+        return (mPlayingState == RUNNING || mPlayingState == ENDED || mPlayingState == CANCELED);
+    }
+
+    /**
+     * Plays the ValueAnimator in reverse. If the animation is already running,
+     * it will stop itself and play backwards from the point reached when reverse was called.
+     * If the animation is not currently running, then it will start from the end and
+     * play backwards. This behavior is only set for the current animation; future playing
+     * of the animation will use the default behavior of playing forward.
+     */
+    public void reverse() {
+        mPlayingBackwards = !mPlayingBackwards;
+        if (mPlayingState == RUNNING) {
+            long currentTime = AnimationUtils.currentAnimationTimeMillis();
+            long currentPlayTime = currentTime - mStartTime;
+            long timeLeft = mDuration - currentPlayTime;
+            mStartTime = currentTime - timeLeft;
+        } else {
+            start(true);
+        }
+    }
+
+    /**
+     * Called internally to end an animation by removing it from the animations list. Must be
+     * called on the UI thread.
+     */
+    private void endAnimation() {
+        sAnimations.remove(this);
+        mPlayingState = STOPPED;
+        if (mListeners != null) {
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            for (AnimatorListener listener : tmpListeners) {
+                listener.onAnimationEnd(this);
+            }
+        }
+    }
+
+    /**
+     * Called internally to start an animation by adding it to the active animations list. Must be
+     * called on the UI thread.
+     */
+    private void startAnimation() {
+        initAnimation();
+        sAnimations.add(this);
+        if (mListeners != null) {
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            for (AnimatorListener listener : tmpListeners) {
+                listener.onAnimationStart(this);
+            }
+        }
+    }
+
+    /**
+     * Internal function called to process an animation frame on an animation that is currently
+     * sleeping through its <code>startDelay</code> phase. The return value indicates whether it
+     * should be woken up and put on the active animations queue.
+     *
+     * @param currentTime The current animation time, used to calculate whether the animation
+     * has exceeded its <code>startDelay</code> and should be started.
+     * @return True if the animation's <code>startDelay</code> has been exceeded and the animation
+     * should be added to the set of active animations.
+     */
+    private boolean delayedAnimationFrame(long currentTime) {
+        if (mPlayingState == CANCELED || mPlayingState == ENDED) {
+            // end the delay, process an animation frame to actually cancel it
+            return true;
+        }
+        if (!mStartedDelay) {
+            mStartedDelay = true;
+            mDelayStartTime = currentTime;
+        } else {
+            long deltaTime = currentTime - mDelayStartTime;
+            if (deltaTime > mStartDelay) {
+                // startDelay ended - start the anim and record the
+                // mStartTime appropriately
+                mStartTime = currentTime - (deltaTime - mStartDelay);
+                mPlayingState = RUNNING;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * This internal function processes a single animation frame for a given animation. The
+     * currentTime parameter is the timing pulse sent by the handler, used to calculate the
+     * elapsed duration, and therefore
+     * the elapsed fraction, of the animation. The return value indicates whether the animation
+     * should be ended (which happens when the elapsed time of the animation exceeds the
+     * animation's duration, including the repeatCount).
+     *
+     * @param currentTime The current time, as tracked by the static timing handler
+     * @return true if the animation's duration, including any repetitions due to
+     * <code>repeatCount</code> has been exceeded and the animation should be ended.
+     */
+    private boolean animationFrame(long currentTime) {
+        boolean done = false;
+
+        if (mPlayingState == STOPPED) {
+            mPlayingState = RUNNING;
+            if (mSeekTime < 0) {
+                mStartTime = currentTime;
+            } else {
+                mStartTime = currentTime - mSeekTime;
+                // Now that we're playing, reset the seek time
+                mSeekTime = -1;
+            }
+        }
+        switch (mPlayingState) {
+        case RUNNING:
+        case SEEKED:
+            float fraction = (float)(currentTime - mStartTime) / mDuration;
+            if (fraction >= 1f) {
+                if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
+                    // Time to repeat
+                    if (mListeners != null) {
+                        for (AnimatorListener listener : mListeners) {
+                            listener.onAnimationRepeat(this);
+                        }
+                    }
+                    ++mCurrentIteration;
+                    if (mRepeatMode == REVERSE) {
+                        mPlayingBackwards = mPlayingBackwards ? false : true;
+                    }
+                    // TODO: doesn't account for fraction going Wayyyyy over 1, like 2+
+                    fraction = fraction - 1f;
+                    mStartTime += mDuration;
+                } else {
+                    done = true;
+                    fraction = Math.min(fraction, 1.0f);
+                }
+            }
+            if (mPlayingBackwards) {
+                fraction = 1f - fraction;
+            }
+            animateValue(fraction);
+            break;
+        case ENDED:
+            // The final value set on the target varies, depending on whether the animation
+            // was supposed to repeat an odd number of times
+            if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
+                animateValue(0f);
+            } else {
+                animateValue(1f);
+            }
+            // Fall through to set done flag
+        case CANCELED:
+            done = true;
+            mPlayingState = STOPPED;
+            break;
+        }
+
+        return done;
+    }
+
+    /**
+     * This method is called with the elapsed fraction of the animation during every
+     * animation frame. This function turns the elapsed fraction into an interpolated fraction
+     * and then into an animated value (from the evaluator. The function is called mostly during
+     * animation updates, but it is also called when the <code>end()</code>
+     * function is called, to set the final value on the property.
+     *
+     * <p>Overrides of this method must call the superclass to perform the calculation
+     * of the animated value.</p>
+     *
+     * @param fraction The elapsed fraction of the animation.
+     */
+    void animateValue(float fraction) {
+        fraction = mInterpolator.getInterpolation(fraction);
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].calculateValue(fraction);
+        }
+        if (mUpdateListeners != null) {
+            int numListeners = mUpdateListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                mUpdateListeners.get(i).onAnimationUpdate(this);
+            }
+        }
+    }
+
+    @Override
+    public ValueAnimator clone() {
+        final ValueAnimator anim = (ValueAnimator) super.clone();
+        if (mUpdateListeners != null) {
+            ArrayList<AnimatorUpdateListener> oldListeners = mUpdateListeners;
+            anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
+            int numListeners = oldListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                anim.mUpdateListeners.add(oldListeners.get(i));
+            }
+        }
+        anim.mSeekTime = -1;
+        anim.mPlayingBackwards = false;
+        anim.mCurrentIteration = 0;
+        anim.mInitialized = false;
+        anim.mPlayingState = STOPPED;
+        anim.mStartedDelay = false;
+        PropertyValuesHolder[] oldValues = mValues;
+        if (oldValues != null) {
+            int numValues = oldValues.length;
+            anim.mValues = new PropertyValuesHolder[numValues];
+            for (int i = 0; i < numValues; ++i) {
+                anim.mValues[i] = oldValues[i].clone();
+            }
+            anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
+            for (int i = 0; i < numValues; ++i) {
+                PropertyValuesHolder valuesHolder = mValues[i];
+                anim.mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
+            }
+        }
+        return anim;
+    }
+
+    /**
+     * Implementors of this interface can add themselves as update listeners
+     * to an <code>ValueAnimator</code> instance to receive callbacks on every animation
+     * frame, after the current frame's values have been calculated for that
+     * <code>ValueAnimator</code>.
+     */
+    public static interface AnimatorUpdateListener {
+        /**
+         * <p>Notifies the occurrence of another frame of the animation.</p>
+         *
+         * @param animation The animation which was repeated.
+         */
+        void onAnimationUpdate(ValueAnimator animation);
+
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 38086f0..29f2e30 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -123,72 +123,6 @@
     public abstract int getSelectedNavigationItem();
 
     /**
-     * Set the action bar into standard navigation mode, supplying a title and subtitle.
-     * 
-     * Standard navigation mode is default. The title is automatically set to the
-     * name of your Activity. Subtitles are displayed underneath the title, usually
-     * in a smaller font or otherwise less prominently than the title. Subtitles are
-     * good for extended descriptions of activity state.
-     *
-     * @param title The action bar's title. null is treated as an empty string.
-     * @param subtitle The action bar's subtitle. null will remove the subtitle entirely.
-     *
-     * @see #setStandardNavigationMode()
-     * @see #setStandardNavigationMode(CharSequence)
-     * @see #setStandardNavigationMode(int)
-     * @see #setStandardNavigationMode(int, int)
-     */
-    public abstract void setStandardNavigationMode(CharSequence title, CharSequence subtitle);
-
-    /**
-     * Set the action bar into standard navigation mode, supplying a title and subtitle.
-     * 
-     * Standard navigation mode is default. The title is automatically set to the
-     * name of your Activity. Subtitles are displayed underneath the title, usually
-     * in a smaller font or otherwise less prominently than the title. Subtitles are
-     * good for extended descriptions of activity state.
-     *
-     * @param titleResId Resource ID of a title string
-     * @param subtitleResId Resource ID of a subtitle string
-     *
-     * @see #setStandardNavigationMode()
-     * @see #setStandardNavigationMode(CharSequence)
-     * @see #setStandardNavigationMode(CharSequence, CharSequence)
-     * @see #setStandardNavigationMode(int)
-     */
-    public abstract void setStandardNavigationMode(int titleResId, int subtitleResId);
-
-    /**
-     * Set the action bar into standard navigation mode, supplying a title and subtitle.
-     * 
-     * Standard navigation mode is default. The title is automatically set to the
-     * name of your Activity on startup if an action bar is present.
-     *
-     * @param title The action bar's title. null is treated as an empty string.
-     *
-     * @see #setStandardNavigationMode()
-     * @see #setStandardNavigationMode(CharSequence, CharSequence)
-     * @see #setStandardNavigationMode(int)
-     * @see #setStandardNavigationMode(int, int)
-     */
-    public abstract void setStandardNavigationMode(CharSequence title);
-
-    /**
-     * Set the action bar into standard navigation mode, supplying a title and subtitle.
-     * 
-     * Standard navigation mode is default. The title is automatically set to the
-     * name of your Activity on startup if an action bar is present.
-     *
-     * @param titleResId Resource ID of a title string
-     *
-     * @see #setStandardNavigationMode()
-     * @see #setStandardNavigationMode(CharSequence)
-     * @see #setStandardNavigationMode(CharSequence, CharSequence)
-     * @see #setStandardNavigationMode(int, int)
-     */
-    public abstract void setStandardNavigationMode(int titleResId);
-
-    /**
      * Set the action bar into standard navigation mode, using the currently set title
      * and/or subtitle.
      *
@@ -324,18 +258,6 @@
     public abstract void setTabNavigationMode();
 
     /**
-     * Set the action bar into tabbed navigation mode.
-     *
-     * @param containerViewId Id of the container view where tab content fragments should appear.
-     *
-     * @see #addTab(Tab)
-     * @see #insertTab(Tab, int)
-     * @see #removeTab(Tab)
-     * @see #removeTabAt(int)
-     */
-    public abstract void setTabNavigationMode(int containerViewId);
-
-    /**
      * Create and return a new {@link Tab}.
      * This tab will not be included in the action bar until it is added.
      *
@@ -354,13 +276,13 @@
     public abstract void addTab(Tab tab);
 
     /**
-     * Insert a tab for use in tabbed navigation mode. The tab will be inserted at
+     * Add a tab for use in tabbed navigation mode. The tab will be inserted at
      * <code>position</code>.
      *
      * @param tab The tab to add
      * @param position The new position of the tab
      */
-    public abstract void insertTab(Tab tab, int position);
+    public abstract void addTab(Tab tab, int position);
 
     /**
      * Remove a tab from the action bar.
@@ -384,6 +306,14 @@
     public abstract void selectTab(Tab tab);
 
     /**
+     * Returns the currently selected tab if in tabbed navigation mode and there is at least
+     * one tab present.
+     *
+     * @return The currently selected tab or null
+     */
+    public abstract Tab getSelectedTab();
+
+    /**
      * Retrieve the current height of the ActionBar.
      *
      * @return The ActionBar's height
@@ -477,22 +407,68 @@
         public abstract void setText(CharSequence text);
 
         /**
-         * Returns the fragment that will be shown when this tab is selected.
+         * Set a custom view to be used for this tab. This overrides values set by
+         * {@link #setText(CharSequence)} and {@link #setIcon(Drawable)}.
          *
-         * @return Fragment associated with this tab
+         * @param view Custom view to be used as a tab.
          */
-        public abstract Fragment getFragment();
+        public abstract void setCustomView(View view);
 
         /**
-         * Set the fragment that will be shown when this tab is selected.
+         * Retrieve a previously set custom view for this tab.
          *
-         * @param fragment Fragment to associate with this tab
+         * @return The custom view set by {@link #setCustomView(View)}.
          */
-        public abstract void setFragment(Fragment fragment);
+        public abstract View getCustomView();
+
+        /**
+         * Give this Tab an arbitrary object to hold for later use.
+         *
+         * @param obj Object to store
+         */
+        public abstract void setTag(Object obj);
+
+        /**
+         * @return This Tab's tag object.
+         */
+        public abstract Object getTag();
+
+        /**
+         * Set the {@link TabListener} that will handle switching to and from this tab.
+         * All tabs must have a TabListener set before being added to the ActionBar.
+         *
+         * @param listener Listener to handle tab selection events
+         */
+        public abstract void setTabListener(TabListener listener);
 
         /**
          * Select this tab. Only valid if the tab has been added to the action bar.
          */
         public abstract void select();
     }
+
+    /**
+     * Callback interface invoked when a tab is focused, unfocused, added, or removed.
+     */
+    public interface TabListener {
+        /**
+         * Called when a tab enters the selected state.
+         *
+         * @param tab The tab that was selected
+         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+         *        during a tab switch. The previous tab's unselect and this tab's select will be
+         *        executed in a single transaction.
+         */
+        public void onTabSelected(Tab tab, FragmentTransaction ft);
+
+        /**
+         * Called when a tab exits the selected state.
+         *
+         * @param tab The tab that was unselected
+         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+         *        during a tab switch. This tab's unselect and the newly selected tab's select
+         *        will be executed in a single transaction.
+         */
+        public void onTabUnselected(Tab tab, FragmentTransaction ft);
+    }
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3fe971f..3dcdc0a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -617,8 +617,6 @@
     /** Start of user-defined activity results. */
     public static final int RESULT_FIRST_USER   = 1;
 
-    private static long sInstanceCount = 0;
-
     private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
     private static final String FRAGMENTS_TAG = "android:fragments";
     private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
@@ -709,23 +707,6 @@
     private Thread mUiThread;
     final Handler mHandler = new Handler();
 
-    // Used for debug only
-    /*
-    public Activity() {
-        ++sInstanceCount;
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        --sInstanceCount;
-    }
-    */
-
-    public static long getInstanceCount() {
-        return sInstanceCount;
-    }
-
     /** Return the intent that started this activity. */
     public Intent getIntent() {
         return mIntent;
@@ -1751,6 +1732,9 @@
     }
 
     /**
+     * @deprecated This functionality will be removed in the future; please do
+     * not use.
+     *
      * Control whether this activity is required to be persistent.  By default
      * activities are not persistent; setting this to true will prevent the
      * system from stopping this activity or its process when running low on
@@ -1765,6 +1749,7 @@
      *                     persistent, true if so, false for the normal
      *                     behavior.
      */
+    @Deprecated
     public void setPersistent(boolean isPersistent) {
         if (mParent == null) {
             try {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d5741fc..4736404 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -755,14 +755,17 @@
         public String pkgList[];
         
         /**
-         * Constant for {@link #flags}: this is a heavy-weight process,
-         * meaning it will not be killed while in the background.
+         * Constant for {@link #flags}: this is an app that is unable to
+         * correctly save its state when going to the background,
+         * so it can not be killed while in the background.
+         * @hide
          */
-        public static final int FLAG_HEAVY_WEIGHT = 1<<0;
+        public static final int FLAG_CANT_SAVE_STATE = 1<<0;
         
         /**
          * Flags of information.  May be any of
-         * {@link #FLAG_HEAVY_WEIGHT}.
+         * {@link #FLAG_CANT_SAVE_STATE}.
+         * @hide
          */
         public int flags;
         
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index fe179d1..e4455fb 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -63,6 +63,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
+import android.view.HardwareRenderer;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewManager;
@@ -75,6 +76,7 @@
 import com.android.internal.os.RuntimeInit;
 import com.android.internal.os.SamplingProfilerIntegration;
 
+import dalvik.system.VMDebug;
 import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
 
 import java.io.File;
@@ -686,8 +688,8 @@
             long dalvikAllocated = dalvikMax - dalvikFree;
             long viewInstanceCount = ViewDebug.getViewInstanceCount();
             long viewRootInstanceCount = ViewDebug.getViewRootInstanceCount();
-            long appContextInstanceCount = ContextImpl.getInstanceCount();
-            long activityInstanceCount = Activity.getInstanceCount();
+            long appContextInstanceCount = VMDebug.countInstancesOfClass(ContextImpl.class);
+            long activityInstanceCount = VMDebug.countInstancesOfClass(Activity.class);
             int globalAssetCount = AssetManager.getGlobalAssetCount();
             int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount();
             int binderLocalObjectCount = Debug.getBinderLocalObjectCount();
@@ -3632,6 +3634,7 @@
     }
 
     public static final ActivityThread systemMain() {
+        HardwareRenderer.disable();
         ActivityThread thread = new ActivityThread();
         thread.attach(true);
         return thread;
diff --git a/core/java/android/app/BackStackEntry.java b/core/java/android/app/BackStackEntry.java
index 00c2fc4..71fd5e5 100644
--- a/core/java/android/app/BackStackEntry.java
+++ b/core/java/android/app/BackStackEntry.java
@@ -490,4 +490,8 @@
     public int getTransitionStyle() {
         return mTransitionStyle;
     }
+
+    public boolean isEmpty() {
+        return mNumOp == 0;
+    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 18ecc24..7497136 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -214,23 +214,8 @@
     private File mExternalFilesDir;
     private File mExternalCacheDir;
 
-    private static long sInstanceCount = 0;
-
     private static final String[] EMPTY_FILE_LIST = {};
 
-    // For debug only
-    /*
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        --sInstanceCount;
-    }
-    */
-
-    public static long getInstanceCount() {
-        return sInstanceCount;
-    }
-
     @Override
     public AssetManager getAssets() {
         return mResources.getAssets();
@@ -338,54 +323,54 @@
     @Override
     public SharedPreferences getSharedPreferences(String name, int mode) {
         SharedPreferencesImpl sp;
+        File prefsFile;
+        boolean needInitialLoad = false;
         synchronized (sSharedPrefs) {
             sp = sSharedPrefs.get(name);
-            if (sp != null && !sp.hasFileChanged()) {
-                //Log.i(TAG, "Returning existing prefs " + name + ": " + sp);
+            if (sp != null && !sp.hasFileChangedUnexpectedly()) {
                 return sp;
             }
-        }
-        File f = getSharedPrefsFile(name);
-        FileInputStream str = null;
-        File backup = makeBackupFile(f);
-        if (backup.exists()) {
-            f.delete();
-            backup.renameTo(f);
-        }
-
-        // Debugging
-        if (f.exists() && !f.canRead()) {
-            Log.w(TAG, "Attempt to read preferences file " + f + " without permission");
-        }
-
-        Map map = null;
-        if (f.exists() && f.canRead()) {
-            try {
-                str = new FileInputStream(f);
-                map = XmlUtils.readMapXml(str);
-                str.close();
-            } catch (org.xmlpull.v1.XmlPullParserException e) {
-                Log.w(TAG, "getSharedPreferences", e);
-            } catch (FileNotFoundException e) {
-                Log.w(TAG, "getSharedPreferences", e);
-            } catch (IOException e) {
-                Log.w(TAG, "getSharedPreferences", e);
+            prefsFile = getSharedPrefsFile(name);
+            if (sp == null) {
+                sp = new SharedPreferencesImpl(prefsFile, mode, null);
+                sSharedPrefs.put(name, sp);
+                needInitialLoad = true;
             }
         }
 
-        synchronized (sSharedPrefs) {
-            if (sp != null) {
-                //Log.i(TAG, "Updating existing prefs " + name + " " + sp + ": " + map);
-                sp.replace(map);
-            } else {
-                sp = sSharedPrefs.get(name);
-                if (sp == null) {
-                    sp = new SharedPreferencesImpl(f, mode, map);
-                    sSharedPrefs.put(name, sp);
+        synchronized (sp) {
+            if (needInitialLoad && sp.isLoaded()) {
+                // lost the race to load; another thread handled it
+                return sp;
+            }
+            File backup = makeBackupFile(prefsFile);
+            if (backup.exists()) {
+                prefsFile.delete();
+                backup.renameTo(prefsFile);
+            }
+
+            // Debugging
+            if (prefsFile.exists() && !prefsFile.canRead()) {
+                Log.w(TAG, "Attempt to read preferences file " + prefsFile + " without permission");
+            }
+
+            Map map = null;
+            if (prefsFile.exists() && prefsFile.canRead()) {
+                try {
+                    FileInputStream str = new FileInputStream(prefsFile);
+                    map = XmlUtils.readMapXml(str);
+                    str.close();
+                } catch (org.xmlpull.v1.XmlPullParserException e) {
+                    Log.w(TAG, "getSharedPreferences", e);
+                } catch (FileNotFoundException e) {
+                    Log.w(TAG, "getSharedPreferences", e);
+                } catch (IOException e) {
+                    Log.w(TAG, "getSharedPreferences", e);
                 }
             }
-            return sp;
+            sp.replace(map);
         }
+        return sp;
     }
 
     private File getPreferencesDir() {
@@ -1510,8 +1495,6 @@
     }
 
     ContextImpl() {
-        // For debug only
-        //++sInstanceCount;
         mOuterContext = this;
     }
 
@@ -1522,7 +1505,6 @@
      * @param context Existing application context.
      */
     public ContextImpl(ContextImpl context) {
-        ++sInstanceCount;
         mPackageInfo = context.mPackageInfo;
         mResources = context.mResources;
         mMainThread = context.mMainThread;
@@ -2736,6 +2718,10 @@
 
     private static final class SharedPreferencesImpl implements SharedPreferences {
 
+        // Lock ordering rules:
+        //  - acquire SharedPreferencesImpl.this before EditorImpl.this
+        //  - acquire mWritingToDiskLock before EditorImpl.this
+
         private final File mFile;
         private final File mBackupFile;
         private final int mMode;
@@ -2743,6 +2729,7 @@
         private Map<String, Object> mMap;  // guarded by 'this'
         private long mTimestamp;  // guarded by 'this'
         private int mDiskWritesInFlight = 0;  // guarded by 'this'
+        private boolean mLoaded = false;  // guarded by 'this'
 
         private final Object mWritingToDiskLock = new Object();
         private static final Object mContent = new Object();
@@ -2753,6 +2740,7 @@
             mFile = file;
             mBackupFile = makeBackupFile(file);
             mMode = mode;
+            mLoaded = initialContents != null;
             mMap = initialContents != null ? initialContents : new HashMap<String, Object>();
             FileStatus stat = new FileStatus();
             if (FileUtils.getFileStatus(file.getPath(), stat)) {
@@ -2761,7 +2749,23 @@
             mListeners = new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
         }
 
-        public boolean hasFileChanged() {
+        // Has this SharedPreferences ever had values assigned to it?
+        boolean isLoaded() {
+            synchronized (this) {
+                return mLoaded;
+            }
+        }
+
+        // Has the file changed out from under us?  i.e. writes that
+        // we didn't instigate.
+        public boolean hasFileChangedUnexpectedly() {
+            synchronized (this) {
+                if (mDiskWritesInFlight > 0) {
+                    // If we know we caused it, it's not unexpected.
+                    Log.d(TAG, "disk write in flight, not unexpected.");
+                    return false;
+                }
+            }
             FileStatus stat = new FileStatus();
             if (!FileUtils.getFileStatus(mFile.getPath(), stat)) {
                 return true;
@@ -2772,8 +2776,9 @@
         }
 
         public void replace(Map newContents) {
-            if (newContents != null) {
-                synchronized (this) {
+            synchronized (this) {
+                mLoaded = true;
+                if (newContents != null) {
                     mMap = newContents;
                 }
             }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 56cf399..16d105a 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -16,7 +16,7 @@
 
 package android.app;
 
-import android.animation.Animatable;
+import android.animation.Animator;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.Intent;
@@ -26,7 +26,6 @@
 import android.os.Parcelable;
 import android.util.AndroidRuntimeException;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.SparseArray;
 import android.view.ContextMenu;
 import android.view.LayoutInflater;
@@ -755,7 +754,7 @@
     /**
      * Called when a fragment loads an animation.
      */
-    public Animatable onCreateAnimatable(int transit, boolean enter, int nextAnim) {
+    public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
         return null;
     }
     
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 7641f61..4d4f892 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -16,10 +16,10 @@
 
 package android.app;
 
-import android.animation.Animatable;
-import android.animation.AnimatableInflater;
-import android.animation.PropertyAnimator;
-import android.animation.Sequencer;
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.content.res.TypedArray;
 import android.os.Bundle;
 import android.os.Handler;
@@ -248,16 +248,16 @@
         return f;
     }
 
-    Animatable loadAnimatable(Fragment fragment, int transit, boolean enter,
+    Animator loadAnimator(Fragment fragment, int transit, boolean enter,
             int transitionStyle) {
-        Animatable animObj = fragment.onCreateAnimatable(transit, enter,
+        Animator animObj = fragment.onCreateAnimator(transit, enter,
                 fragment.mNextAnim);
         if (animObj != null) {
             return animObj;
         }
         
         if (fragment.mNextAnim != 0) {
-            Animatable anim = AnimatableInflater.loadAnimatable(mActivity, fragment.mNextAnim);
+            Animator anim = AnimatorInflater.loadAnimator(mActivity, fragment.mNextAnim);
             if (anim != null) {
                 return anim;
             }
@@ -288,7 +288,7 @@
             return null;
         }
         
-        return AnimatableInflater.loadAnimatable(mActivity, anim);
+        return AnimatorInflater.loadAnimator(mActivity, anim);
     }
     
     void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
@@ -360,13 +360,13 @@
                             if (f.mView != null) {
                                 f.mView.setSaveFromParentEnabled(false);
                                 if (container != null) {
-                                    Animatable anim = loadAnimatable(f, transit, true,
+                                    Animator anim = loadAnimator(f, transit, true,
                                             transitionStyle);
                                     if (anim != null) {
-                                        if (anim instanceof Sequencer) {
-                                            ((Sequencer)anim).setTarget(f.mView);
-                                        } else if (anim instanceof PropertyAnimator) {
-                                            ((PropertyAnimator)anim).setTarget(f.mView);
+                                        if (anim instanceof AnimatorSet) {
+                                            ((AnimatorSet)anim).setTarget(f.mView);
+                                        } else if (anim instanceof ObjectAnimator) {
+                                            ((ObjectAnimator)anim).setTarget(f.mView);
                                         }
                                         anim.start();
                                     }
@@ -448,13 +448,13 @@
                         }
                         if (f.mView != null && f.mContainer != null) {
                             if (mCurState > Fragment.INITIALIZING) {
-                                Animatable anim = loadAnimatable(f, transit, true,
+                                Animator anim = loadAnimator(f, transit, true,
                                         transitionStyle);
                                 if (anim != null) {
-                                    if (anim instanceof Sequencer) {
-                                        ((Sequencer)anim).setTarget(f.mView);
-                                    } else if (anim instanceof PropertyAnimator) {
-                                        ((PropertyAnimator)anim).setTarget(f.mView);
+                                    if (anim instanceof AnimatorSet) {
+                                        ((AnimatorSet)anim).setTarget(f.mView);
+                                    } else if (anim instanceof ObjectAnimator) {
+                                        ((ObjectAnimator)anim).setTarget(f.mView);
                                     }
                                     anim.start();
                                 }
@@ -588,13 +588,13 @@
         if (!fragment.mHidden) {
             fragment.mHidden = true;
             if (fragment.mView != null) {
-                Animatable anim = loadAnimatable(fragment, transition, true,
+                Animator anim = loadAnimator(fragment, transition, true,
                         transitionStyle);
                 if (anim != null) {
-                    if (anim instanceof Sequencer) {
-                        ((Sequencer)anim).setTarget(fragment.mView);
-                    } else if (anim instanceof PropertyAnimator) {
-                        ((PropertyAnimator)anim).setTarget(fragment.mView);
+                    if (anim instanceof AnimatorSet) {
+                        ((AnimatorSet)anim).setTarget(fragment.mView);
+                    } else if (anim instanceof ObjectAnimator) {
+                        ((ObjectAnimator)anim).setTarget(fragment.mView);
                     }
                     anim.start();
                 }
@@ -612,13 +612,13 @@
         if (fragment.mHidden) {
             fragment.mHidden = false;
             if (fragment.mView != null) {
-                Animatable anim = loadAnimatable(fragment, transition, true,
+                Animator anim = loadAnimator(fragment, transition, true,
                         transitionStyle);
                 if (anim != null) {
-                    if (anim instanceof Sequencer) {
-                        ((Sequencer)anim).setTarget(fragment.mView);
-                    } else if (anim instanceof PropertyAnimator) {
-                        ((PropertyAnimator)anim).setTarget(fragment.mView);
+                    if (anim instanceof AnimatorSet) {
+                        ((AnimatorSet)anim).setTarget(fragment.mView);
+                    } else if (anim instanceof ObjectAnimator) {
+                        ((ObjectAnimator)anim).setTarget(fragment.mView);
                     }
                     anim.start();
                 }
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 04598a3..9d44106 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -85,6 +85,12 @@
      * @return Returns the same FragmentTransaction instance.
      */
     public FragmentTransaction show(Fragment fragment);
+
+    /**
+     * @return <code>true</code> if this transaction contains no operations,
+     *         <code>false</code> otherwise.
+     */
+    public boolean isEmpty();
     
     /**
      * Bit mask that is set for all enter transitions.
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 4dc88b3..d7a0412 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -3,8 +3,6 @@
 import com.android.internal.view.IInputMethodCallback;
 import com.android.internal.view.IInputMethodSession;
 
-import dalvik.system.PathClassLoader;
-
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -168,17 +166,14 @@
             // If the application does not have (Java) code, then no ClassLoader
             // has been set up for it.  We will need to do our own search for
             // the native code.
-            path = ai.applicationInfo.dataDir + "/lib/" + System.mapLibraryName(libname);
-            if (!(new File(path)).exists()) {
-                path = null;
+            File libraryFile = new File(ai.applicationInfo.nativeLibraryDir,
+                    System.mapLibraryName(libname));
+            if (libraryFile.exists()) {
+                path = libraryFile.getPath();
             }
         }
         
         if (path == null) {
-            path = ((PathClassLoader)getClassLoader()).findLibrary(libname);
-        }
-        
-        if (path == null) {
             throw new IllegalArgumentException("Unable to find native library: " + libname);
         }
         
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 83a2024..9f7bef4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -109,11 +109,17 @@
     public PendingIntent deleteIntent;
 
     /**
-     * An intent to launch instead of posting the notification to the status bar. Only for use with
-     * extremely high-priority notifications demanding the user's attention, such as an incoming
+     * An intent to launch instead of posting the notification to the status bar.
+     * Only for use with extremely high-priority notifications demanding the user's
+     * <strong>immediate</strong>attention, such as an incoming phone call or
+     * alarm clock that the user has explicitly set to a particular time.
      * call (handled in the core Android Phone app with a full-screen Activity).
-     * Use with {@link #FLAG_HIGH_PRIORITY} to ensure that this notification will reach the user
-     * even when other notifications are suppressed.
+     * If this facility is used for something else, please give the user an option
+     * to turn it off and use a normal notification, as this can be extremely
+     * disruptive.
+     * 
+     * <p>Use with {@link #FLAG_HIGH_PRIORITY} to ensure that this notification
+     * will reach the user even when other notifications are suppressed.
      */
     public PendingIntent fullScreenIntent;
 
diff --git a/core/java/android/app/backup/SharedPreferencesBackupHelper.java b/core/java/android/app/backup/SharedPreferencesBackupHelper.java
index 23b1703..213bd31 100644
--- a/core/java/android/app/backup/SharedPreferencesBackupHelper.java
+++ b/core/java/android/app/backup/SharedPreferencesBackupHelper.java
@@ -16,6 +16,7 @@
 
 package android.app.backup;
 
+import android.app.QueuedWork;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.os.ParcelFileDescriptor;
@@ -94,7 +95,11 @@
     public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState) {
         Context context = mContext;
-        
+
+        // If a SharedPreference has an outstanding write in flight,
+        // wait for it to finish flushing to disk.
+        QueuedWork.waitToFinish();
+
         // make filenames for the prefGroups
         String[] prefGroups = mPrefGroups;
         final int N = prefGroups.length;
@@ -123,4 +128,3 @@
         }
     }
 }
-
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 952765d..9d0b3f2 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -173,9 +173,9 @@
         Log.d(TAG, msg);
     }
 
-    public void setBluetoothTethering(boolean value, String uuid, String bridge) {
+    public void setBluetoothTethering(boolean value) {
         try {
-            mService.setBluetoothTethering(value, uuid, bridge);
+            mService.setBluetoothTethering(value);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index f8f678b..c4a40cd 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -83,7 +83,7 @@
     int getInputDevicePriority(in BluetoothDevice device);
 
     boolean isTetheringOn();
-    void setBluetoothTethering(boolean value, String uuid, String bridge);
+    void setBluetoothTethering(boolean value);
     int getPanDeviceState(in BluetoothDevice device);
     BluetoothDevice[] getConnectedPanDevices();
     boolean connectPanDevice(in BluetoothDevice device);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 359eaaa..4d0b8b0 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1577,6 +1577,30 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK";
     /**
+     * Broadcast Action:  A sticky broadcast that indicates a memory full
+     * condition on the device. This is intended for activities that want
+     * to be able to fill the data partition completely, leaving only
+     * enough free space to prevent system-wide SQLite failures.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * {@hide}
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEVICE_STORAGE_FULL = "android.intent.action.DEVICE_STORAGE_FULL";
+    /**
+     * Broadcast Action:  Indicates memory full condition on the device
+     * no longer exists.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * {@hide}
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEVICE_STORAGE_NOT_FULL = "android.intent.action.DEVICE_STORAGE_NOT_FULL";
+    /**
      * Broadcast Action:  Indicates low memory condition notification acknowledged by user
      * and package management should be started.
      * This is triggered by the user from the ACTION_DEVICE_STORAGE_LOW
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7f166cf..8963d0c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -705,10 +705,11 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
-     * {@link #hasSystemFeature}: The device includes a magnetometer (compass).
+     * {@link #hasSystemFeature}: The device can communicate using Near-Field
+     * Communications (NFC).
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass";
+    public static final String FEATURE_NFC = "android.hardware.nfc";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
@@ -719,6 +720,28 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a barometer (air
+     * pressure sensor.)
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a magnetometer (compass).
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a gyroscope.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device includes a light sensor.
      */
     @SdkConstant(SdkConstantType.FEATURE)
@@ -752,7 +775,21 @@
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
-    
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The SIP API is enabled on the device.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SIP = "android.software.sip";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports SIP-based VOIP.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SIP_VOIP = "android.software.sip.voip";
+
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device's display has a touch screen.
@@ -779,6 +816,15 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's touch screen is capable of
+     * tracking a full hand of fingers fully independently -- that is, 5 or
+     * more simultaneous independent pointers.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND = "android.hardware.touchscreen.multitouch.jazzhand";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports live wallpapers.
      */
     @SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index 1bb3857..d4e5cc1 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -20,6 +20,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.ref.SoftReference;
 import java.util.Arrays;
 
 /**
@@ -30,7 +31,7 @@
     private final byte[] mSignature;
     private int mHashCode;
     private boolean mHaveHashCode;
-    private String mString;
+    private SoftReference<String> mStringRef;
 
     /**
      * Create Signature from an existing raw byte array.
@@ -96,10 +97,13 @@
      * cached so future calls will return the same String.
      */
     public String toCharsString() {
-        if (mString != null) return mString;
-        String str = new String(toChars());
-        mString = str;
-        return mString;
+        String str = mStringRef == null ? null : mStringRef.get();
+        if (str != null) {
+            return str;
+        }
+        str = new String(toChars());
+        mStringRef = new SoftReference<String>(str);
+        return str;
     }
 
     /**
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 38d6526..90bb0e2 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -769,8 +769,8 @@
      */
     public static void cursorStringToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getString(index));
         }
     }
@@ -785,8 +785,8 @@
      */
     public static void cursorLongToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getLong(index));
         }
     }
@@ -801,8 +801,8 @@
      */
     public static void cursorShortToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getShort(index));
         }
     }
@@ -817,8 +817,8 @@
      */
     public static void cursorIntToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getInt(index));
         }
     }
@@ -833,8 +833,8 @@
      */
     public static void cursorFloatToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getFloat(index));
         }
     }
@@ -849,8 +849,8 @@
      */
     public static void cursorDoubleToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getDouble(index));
         }
     }
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index 1830f6c..21d2ab0 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -30,6 +30,7 @@
 
     public void acquireReference() {
         synchronized(mLock) {
+            checkRefCount();
             if (mReferenceCount <= 0) {
                 throw new IllegalStateException(
                         "attempt to re-open an already-closed object: " + getObjInfo());
@@ -40,6 +41,7 @@
 
     public void releaseReference() {
         synchronized(mLock) {
+            checkRefCount();
             mReferenceCount--;
             if (mReferenceCount == 0) {
                 onAllReferencesReleased();
@@ -49,6 +51,7 @@
 
     public void releaseReferenceFromContainer() {
         synchronized(mLock) {
+            checkRefCount();
             mReferenceCount--;
             if (mReferenceCount == 0) {
                 onAllReferencesReleasedFromContainer();
@@ -63,8 +66,7 @@
         if (this instanceof SQLiteDatabase) {
             buff.append("database = ");
             buff.append(((SQLiteDatabase)this).getPath());
-        } else if (this instanceof SQLiteProgram || this instanceof SQLiteStatement ||
-                this instanceof SQLiteQuery) {
+        } else if (this instanceof SQLiteProgram) {
             buff.append("mSql = ");
             buff.append(((SQLiteProgram)this).mSql);
         } else if (this instanceof CursorWindow) {
@@ -74,4 +76,13 @@
         buff.append(") ");
         return buff.toString();
     }
+
+    private void checkRefCount() {
+        if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
+            if (mReferenceCount > 1000) {
+                throw new IllegalStateException("refcount: " + mReferenceCount + ", " +
+                        getObjInfo());
+            }
+        }
+    }
 }
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index aa0a57d..0b5a4df 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -92,9 +92,6 @@
         // Note that native_finalize() checks to make sure that nStatement is
         // non-null before destroying it.
         if (nStatement != 0) {
-            if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-                Log.v(TAG, "closed and deallocated DbObj (id#" + nStatement +")");
-            }
             mDatabase.finalizeStatementLater(nStatement);
             nStatement = 0;
         }
@@ -109,16 +106,10 @@
             return false;
         }
         mInUse = true;
-        if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-            Log.v(TAG, "Acquired DbObj (id#" + nStatement + ") from DB cache");
-        }
         return true;
     }
 
     /* package */ synchronized void release() {
-        if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-            Log.v(TAG, "Released DbObj (id#" + nStatement + ") back to DB cache");
-        }
         mInUse = false;
     }
 
@@ -144,6 +135,22 @@
         }
     }
 
+    @Override public String toString() {
+        synchronized(this) {
+            StringBuilder buff = new StringBuilder();
+            buff.append(" nStatement=");
+            buff.append(nStatement);
+            buff.append(", db=");
+            buff.append(mDatabase.getPath());
+            buff.append(", db_connectionNum=");
+            buff.append(mDatabase.mConnectionNum);
+            buff.append(", sql=");
+            int len = mSqlStmt.length();
+            buff.append(mSqlStmt.substring(0, (len > 100) ? 100 : len));
+            return buff.toString();
+        }
+    }
+
     /**
      * Compiles SQL into a SQLite program.
      *
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index b83d5ad..6937da0 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -39,7 +39,6 @@
 import java.io.File;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -273,7 +272,7 @@
      * invoked.
      *
      * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
-     * (@link setMaxSqlCacheSize(int)}).
+     * (@link #setMaxSqlCacheSize(int)}).
      */
     // default statement-cache size per database connection ( = instance of this class)
     private int mMaxSqlCacheSize = 25;
@@ -903,8 +902,7 @@
     public interface CursorFactory {
         /**
          * See
-         * {@link SQLiteCursor#SQLiteCursor(SQLiteDatabase, SQLiteCursorDriver,
-         * String, SQLiteQuery)}.
+         * {@link SQLiteCursor#SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)}.
          */
         public Cursor newCursor(SQLiteDatabase db,
                 SQLiteCursorDriver masterQuery, String editTable,
@@ -1068,7 +1066,7 @@
             closePendingStatements();
             releaseCustomFunctions();
             // close this database instance - regardless of its reference count value
-            dbclose();
+            closeDatabase();
             if (mConnectionPool != null) {
                 if (Log.isLoggable(TAG, Log.DEBUG)) {
                     assert mConnectionPool != null;
@@ -1077,7 +1075,7 @@
                 mConnectionPool.close();
             }
         } finally {
-            unlock();
+            unlock();            
         }
     }
 
@@ -1102,6 +1100,47 @@
     }
 
     /**
+     * package level access for testing purposes
+     */
+    /* package */ void closeDatabase() throws SQLiteException {
+        try {
+            dbclose();
+        } catch (SQLiteUnfinalizedObjectsException e)  {
+            String msg = e.getMessage();
+            String[] tokens = msg.split(",", 2);
+            int stmtId = Integer.parseInt(tokens[0]);
+            // get extra info about this statement, if it is still to be released by closeClosable()
+            Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
+            boolean found = false;
+            while (iter.hasNext()) {
+                Map.Entry<SQLiteClosable, Object> entry = iter.next();
+                SQLiteClosable program = entry.getKey();
+                if (program != null && program instanceof SQLiteProgram) {
+                        SQLiteCompiledSql compiledSql = ((SQLiteProgram)program).mCompiledSql;
+                        if (compiledSql.nStatement == stmtId) {
+                            msg = compiledSql.toString();
+                            found = true;
+                        }
+                }
+            }
+            if (!found) {
+                // the statement is already released by closeClosable(). is it waiting to be
+                // finalized?
+                if (mClosedStatementIds.contains(stmtId)) {
+                    Log.w(TAG, "this shouldn't happen. finalizing the statement now: ");
+                    closePendingStatements();
+                    // try to close the database again
+                    closeDatabase();
+                }
+            } else {
+                // the statement is not yet closed. most probably programming error in the app.
+                Log.w(TAG, "dbclose failed due to un-close()d SQL statements: " + msg);
+                throw e;
+            }
+        }
+    }
+
+    /**
      * Native call to close the database.
      */
     private native void dbclose();
@@ -2080,7 +2119,7 @@
                  */
                 if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
                     Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
-                            getPath() + ". Consider increasing cachesize.");
+                            getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. ");
                 }
             } 
             /* add the given SQLiteCompiledSql compiledStatement to cache.
@@ -2142,7 +2181,7 @@
      *
      * @param cacheSize the size of the cache. can be (0 to {@link #MAX_SQL_CACHE_SIZE})
      * @throws IllegalStateException if input cacheSize > {@link #MAX_SQL_CACHE_SIZE} or
-     * > the value set with previous setMaxSqlCacheSize() call.
+     * the value set with previous setMaxSqlCacheSize() call.
      */
     public synchronized void setMaxSqlCacheSize(int cacheSize) {
         if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
@@ -2342,10 +2381,12 @@
 
     /* package */ SQLiteDatabase getDbConnection(String sql) {
         verifyDbIsOpen();
-        // this method should always be called with main database connection handle
-        // NEVER with pooled database connection handle
+        // this method should always be called with main database connection handle.
+        // the only time when it is called with pooled database connection handle is
+        // corruption occurs while trying to open a pooled database connection handle.
+        // in that case, simply return 'this' handle
         if (isPooledConnection()) {
-            throw new IllegalStateException("incorrect database connection handle");
+            return this;
         }
 
         // use the current connection handle if
@@ -2504,12 +2545,18 @@
      */
     public boolean isDatabaseIntegrityOk() {
         verifyDbIsOpen();
-        ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs();
-        if (attachedDbs == null) {
-            throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
-                    "be retrieved. probably because the database is closed");
+        ArrayList<Pair<String, String>> attachedDbs = null;
+        try {
+            attachedDbs = getAttachedDbs();
+            if (attachedDbs == null) {
+                throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
+                        "be retrieved. probably because the database is closed");
+            }
+        } catch (SQLiteException e) {
+            // can't get attachedDb list. do integrity check on the main database
+            attachedDbs = new ArrayList<Pair<String, String>>();
+            attachedDbs.add(new Pair<String, String>("main", this.mPath));
         }
-        boolean isDatabaseCorrupt = false;
         for (int i = 0; i < attachedDbs.size(); i++) {
             Pair<String, String> p = attachedDbs.get(i);
             SQLiteStatement prog = null;
@@ -2518,14 +2565,14 @@
                 String rslt = prog.simpleQueryForString();
                 if (!rslt.equalsIgnoreCase("ok")) {
                     // integrity_checker failed on main or attached databases
-                    isDatabaseCorrupt = true;
                     Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
+                    return false;
                 }
             } finally {
                 if (prog != null) prog.close();
             }
         }
-        return isDatabaseCorrupt;
+        return true;
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteMisuseException.java b/core/java/android/database/sqlite/SQLiteMisuseException.java
index 685f3ea..546ec08 100644
--- a/core/java/android/database/sqlite/SQLiteMisuseException.java
+++ b/core/java/android/database/sqlite/SQLiteMisuseException.java
@@ -16,6 +16,18 @@
 
 package android.database.sqlite;
 
+/**
+ * This error can occur if the application creates a SQLiteStatement object and allows multiple
+ * threads in the application use it at the same time.
+ * Sqlite returns this error if bind and execute methods on this object occur at the same time
+ * from multiple threads, like so:
+ *     thread # 1: in execute() method of the SQLiteStatement object
+ *     while thread # 2: is in bind..() on the same object.
+ *</p>
+ * FIX this by NEVER sharing the same SQLiteStatement object between threads.
+ * Create a local instance of the SQLiteStatement whenever it is needed, use it and close it ASAP.
+ * NEVER make it globally available.
+ */
 public class SQLiteMisuseException extends SQLiteException {
     public SQLiteMisuseException() {}
 
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 9b7d823..bcb0c48 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -52,7 +52,7 @@
     /**
      * the SQLiteCompiledSql object for the given sql statement.
      */
-    private SQLiteCompiledSql mCompiledSql;
+    /* package */ SQLiteCompiledSql mCompiledSql;
 
     /**
      * SQLiteCompiledSql statement id is populated with the corresponding object from the above
@@ -130,10 +130,6 @@
             // make sure it is acquired by me.
             mCompiledSql.acquire();
             mDatabase.addToCompiledQueries(mSql, mCompiledSql);
-            if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-                Log.v(TAG, "Created DbObj (id#" + mCompiledSql.nStatement +
-                        ") for sql: " + mSql);
-            }
         } else {
             // it is already in compiled-sql cache.
             // try to acquire the object.
@@ -144,12 +140,6 @@
                 // CompiledSql object. create a new one.
                 // finalize it when I am done with it in "this" object.
                 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
-                if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-                    Log.v(TAG, "** possible bug ** Created NEW DbObj (id#" +
-                            mCompiledSql.nStatement +
-                            ") because the previously created DbObj (id#" + last +
-                            ") was not released for sql:" + mSql);
-                }
                 // since it is not in the cache, no need to acquire() it.
             }
         }
diff --git a/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java b/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java
new file mode 100644
index 0000000..bcf95e2
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.database.sqlite;
+
+/**
+ * Thrown if the database can't be closed because of some un-closed
+ * Cursor or SQLiteStatement objects. Could happen when a thread is trying to close
+ * the database while another thread still hasn't closed a Cursor on that database.
+ * @hide
+ */
+public class SQLiteUnfinalizedObjectsException extends SQLiteException {
+    public SQLiteUnfinalizedObjectsException() {}
+
+    public SQLiteUnfinalizedObjectsException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index f3f2d60..63bdf8d 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -105,10 +105,9 @@
         mTarget = target;
         mContext = context;
 
-        IntentFilter filter =
-                new IntentFilter(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
-        filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
 
         mContext.registerReceiver(new MobileDataStateReceiver(), filter);
         mMobileDataState = Phone.DataState.DISCONNECTED;
@@ -192,7 +191,7 @@
                                     ConnectivityManager.TYPE_MOBILE);
                                 if (mConnectivityManager == null) {
                                     IBinder b = ServiceManager.getService(
-                                            mContext.CONNECTIVITY_SERVICE);
+                                            Context.CONNECTIVITY_SERVICE);
                                     mConnectivityManager = IConnectivityManager.Stub.asInterface(b);
                                 }
                                 try {
@@ -251,7 +250,6 @@
                         " broadcast" + reason == null ? "" : "(" + reason + ")");
                 setDetailedState(DetailedState.FAILED, reason, apnName);
             }
-            TelephonyManager tm = TelephonyManager.getDefault();
         }
     }
 
@@ -269,15 +267,6 @@
     }
 
     /**
-     * {@inheritDoc}
-     * The mobile data network subtype indicates what generation network technology is in effect,
-     * e.g., GPRS, EDGE, UMTS, etc.
-     */
-    public int getNetworkSubtype() {
-        return TelephonyManager.getDefault().getNetworkType();
-    }
-
-    /**
      * Return the system properties name associated with the tcp buffer sizes
      * for this network.
      */
@@ -338,16 +327,6 @@
      * change from the previous state, send a notification to
      * any listeners.
      * @param state the new @{code DetailedState}
-     */
-    private void setDetailedState(NetworkInfo.DetailedState state) {
-        setDetailedState(state, null, null);
-    }
-
-    /**
-     * Record the detailed state of a network, and if it is a
-     * change from the previous state, send a notification to
-     * any listeners.
-     * @param state the new @{code DetailedState}
      * @param reason a {@code String} indicating a reason for the state change,
      * if one was supplied. May be {@code null}.
      * @param extraInfo optional {@code String} providing extra information about the state change
@@ -372,10 +351,6 @@
         }
     }
 
-    private void setDetailedStateInternal(NetworkInfo.DetailedState state) {
-        mNetworkInfo.setDetailedState(state, null, null);
-    }
-
     public void setTeardownRequested(boolean isRequested) {
         mTeardownRequested = isRequested;
     }
diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java
index 4101ab4..9c4d6e8 100644
--- a/core/java/android/net/WebAddress.java
+++ b/core/java/android/net/WebAddress.java
@@ -56,7 +56,7 @@
     static Pattern sAddressPattern = Pattern.compile(
             /* scheme    */ "(?:(http|https|file)\\:\\/\\/)?" +
             /* authority */ "(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
-            /* host      */ "([-" + GOOD_IRI_CHAR + "%_]+(?:\\.[-" + GOOD_IRI_CHAR + "%_]+)*|\\[[0-9a-fA-F:\\.]+\\])?" +
+            /* host      */ "([" + GOOD_IRI_CHAR + "%_-][" + GOOD_IRI_CHAR + "%_\\.-]*|\\[[0-9a-fA-F:\\.]+\\])?" +
             /* port      */ "(?:\\:([0-9]*))?" +
             /* path      */ "(\\/?[^#]*)?" +
             /* anchor    */ ".*", Pattern.CASE_INSENSITIVE);
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index aadacab..d2f3d64 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -123,6 +123,16 @@
  *     <li>The task can be executed only once (an exception will be thrown if
  *     a second execution is attempted.)</li>
  * </ul>
+ *
+ * <h2>Memory observability</h2>
+ * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
+ * operations are safe without explicit synchronizations.</p>
+ * <ul>
+ *     <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
+ *     in {@link #doInBackground}.
+ *     <li>Set member fields in {@link #doInBackground}, and refer to them in
+ *     {@link #onProgressUpdate} and {@link #onPostExecute}.
+ * </ul>
  */
 public abstract class AsyncTask<Params, Progress, Result> {
     private static final String LOG_TAG = "AsyncTask";
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9fe6e01..32fb108 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -216,6 +216,11 @@
         public abstract Map<Integer, ? extends Sensor> getSensorStats();
 
         /**
+         * Returns a mapping containing active process data.
+         */
+        public abstract SparseArray<? extends Pid> getPidStats();
+        
+        /**
          * Returns a mapping containing process statistics.
          *
          * @return a Map from Strings to Uid.Proc objects.
@@ -286,6 +291,11 @@
             public abstract Timer getSensorTime();
         }
 
+        public class Pid {
+            public long mWakeSum;
+            public long mWakeStart;
+        }
+
         /**
          * The statistics associated with a particular process.
          */
@@ -521,6 +531,11 @@
     public abstract HistoryItem getHistory();
     
     /**
+     * Return the base time offset for the battery history.
+     */
+    public abstract long getHistoryBaseTime();
+    
+    /**
      * Returns the number of times the device has been started.
      */
     public abstract int getStartCount();
@@ -1571,8 +1586,10 @@
                         sb.append(prefix); sb.append("      CPU: ");
                                 formatTime(sb, userTime); sb.append("usr + ");
                                 formatTime(sb, systemTime); sb.append("krn\n");
-                        sb.append(prefix); sb.append("      "); sb.append(starts);
-                                sb.append(" proc starts");
+                        if (starts != 0) {
+                            sb.append(prefix); sb.append("      "); sb.append(starts);
+                                    sb.append(" proc starts");
+                        }
                         pw.println(sb.toString());
                         for (int e=0; e<numExcessive; e++) {
                             Uid.Proc.ExcessiveWake ew = ps.getExcessiveWake(e);
@@ -1673,6 +1690,7 @@
         HistoryItem rec = getHistory();
         if (rec != null) {
             pw.println("Battery History:");
+            long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
             int oldState = 0;
             int oldStatus = -1;
             int oldHealth = -1;
@@ -1681,7 +1699,7 @@
             int oldVolt = -1;
             while (rec != null) {
                 pw.print("  ");
-                pw.print(rec.time);
+                TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
                 pw.print(" ");
                 if (rec.cmd == HistoryItem.CMD_START) {
                     pw.println(" START");
@@ -1784,6 +1802,35 @@
                 oldState = rec.states;
                 rec = rec.next;
             }
+            pw.println("");
+        }
+        
+        SparseArray<? extends Uid> uidStats = getUidStats();
+        final int NU = uidStats.size();
+        boolean didPid = false;
+        long nowRealtime = SystemClock.elapsedRealtime();
+        StringBuilder sb = new StringBuilder(64);
+        for (int i=0; i<NU; i++) {
+            Uid uid = uidStats.valueAt(i);
+            SparseArray<? extends Uid.Pid> pids = uid.getPidStats();
+            if (pids != null) {
+                for (int j=0; j<pids.size(); j++) {
+                    Uid.Pid pid = pids.valueAt(j);
+                    if (!didPid) {
+                        pw.println("Per-PID Stats:");
+                        didPid = true;
+                    }
+                    long time = pid.mWakeSum + (pid.mWakeStart != 0
+                            ? (nowRealtime - pid.mWakeStart) : 0);
+                    pw.print("  PID "); pw.print(pids.keyAt(j));
+                            pw.print(" wake time: ");
+                            TimeUtils.formatDuration(time, pw);
+                            pw.println("");
+                }
+            }
+        }
+        if (didPid) {
+            pw.println("");
         }
         
         pw.println("Statistics since last charge:");
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 4889c19..c28ccd3 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -22,6 +22,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.app.Fragment;
+import android.app.FragmentTransaction;
 import android.app.ListActivity;
 import android.content.Context;
 import android.content.Intent;
@@ -34,12 +35,11 @@
 import android.os.Message;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Xml;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.FrameLayout;
@@ -730,11 +730,26 @@
                 com.android.internal.R.id.prefs, f).commit();
     }
 
+    /**
+     * Start a new fragment.
+     *
+     * @param fragment The fragment to start
+     * @param push If true, the current fragment will be pushed onto the back stack.  If false,
+     * the current fragment will be replaced.
+     */
+    public void startPreferenceFragment(Fragment fragment, boolean push) {
+        FragmentTransaction transaction = getFragmentManager().openTransaction();
+        transaction.replace(com.android.internal.R.id.prefs, fragment);
+        if (push) {
+            transaction.addToBackStack(BACK_STACK_PREFS);
+        }
+        transaction.commit();
+    }
+
     @Override
     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
         Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras());
-        getFragmentManager().openTransaction().replace(com.android.internal.R.id.prefs, f)
-                .addToBackStack(BACK_STACK_PREFS).commit();
+        startPreferenceFragment(f, true);
         return true;
     }
 
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index 2b20946..fa83897 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -443,7 +443,7 @@
             pm.setSharedPreferencesMode(sharedPreferencesMode);
             pm.inflateFromResource(context, resId, null);
 
-            defaultValueSp.edit().putBoolean(KEY_HAS_SET_DEFAULT_VALUES, true).commit();
+            defaultValueSp.edit().putBoolean(KEY_HAS_SET_DEFAULT_VALUES, true).apply();
         }
     }
     
@@ -481,7 +481,7 @@
     
     private void setNoCommit(boolean noCommit) {
         if (!noCommit && mEditor != null) {
-            mEditor.commit();
+            mEditor.apply();
         }
         
         mNoCommit = noCommit;
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index c8e5921..cb6e18f 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -27,6 +27,7 @@
 import android.net.Uri;
 import android.provider.BrowserContract.Bookmarks;
 import android.provider.BrowserContract.History;
+import android.provider.BrowserContract.Searches;
 import android.util.Log;
 import android.webkit.WebIconDatabase;
 
@@ -525,29 +526,11 @@
      * @param search    The string to add to the searches database.
      */
     public static final void addSearchUrl(ContentResolver cr, String search) {
-        long now = System.currentTimeMillis();
-        Cursor c = null;
-        try {
-            c = cr.query(
-                SEARCHES_URI,
-                SEARCHES_PROJECTION,
-                SEARCHES_WHERE_CLAUSE,
-                new String [] { search },
-                null);
-            ContentValues map = new ContentValues();
-            map.put(SearchColumns.SEARCH, search);
-            map.put(SearchColumns.DATE, now);
-            /* We should only get one answer that is exactly the same. */
-            if (c.moveToFirst()) {
-                cr.update(SEARCHES_URI, map, "_id = " + c.getInt(0), null);
-            } else {
-                cr.insert(SEARCHES_URI, map);
-            }
-        } catch (IllegalStateException e) {
-            Log.e(LOGTAG, "addSearchUrl", e);
-        } finally {
-            if (c != null) c.close();
-        }
+        // The content provider will take care of updating existing searches instead of duplicating
+        ContentValues values = new ContentValues();
+        values.put(Searches.SEARCH, search);
+        values.put(Searches.DATE, System.currentTimeMillis());
+        cr.insert(Searches.CONTENT_URI, values);
     }
 
     /**
@@ -559,7 +542,7 @@
         // FIXME: Should this clear the urls to which these searches lead?
         // (i.e. remove google.com/query= blah blah blah)
         try {
-            cr.delete(SEARCHES_URI, null, null);
+            cr.delete(Searches.CONTENT_URI, null, null);
         } catch (IllegalStateException e) {
             Log.e(LOGTAG, "clearSearches", e);
         }
@@ -578,8 +561,7 @@
      */
     public static final void requestAllIcons(ContentResolver cr, String where,
             WebIconDatabase.IconListener listener) {
-        WebIconDatabase.getInstance()
-                .bulkRequestIconForPageUrl(cr, where, listener);
+        WebIconDatabase.getInstance().bulkRequestIconForPageUrl(cr, where, listener);
     }
 
     /**
diff --git a/core/java/android/provider/BrowserContract.java b/core/java/android/provider/BrowserContract.java
index 22bbe68..f8a2a13 100644
--- a/core/java/android/provider/BrowserContract.java
+++ b/core/java/android/provider/BrowserContract.java
@@ -77,11 +77,11 @@
         public static final String FOLDER_NAME_BOOKMARKS = "google_chrome_bookmarks";
         public static final String FOLDER_NAME_BOOKMARKS_BAR = "bookmark_bar";
         public static final String FOLDER_NAME_OTHER_BOOKMARKS = "other_bookmarks";
-        
+
         /** The client unique ID for an item */
         public static final String CLIENT_UNIQUE = BaseSyncColumns.SYNC4;
     }
-    
+
     /**
      * Columns that appear when each row of a table belongs to a specific
      * account, including sync information that an account may need.
@@ -120,6 +120,20 @@
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String DIRTY = "dirty";
+
+        /**
+         * The time that this row was created on its originating client (msecs
+         * since the epoch).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATE_CREATED = "created";
+
+        /**
+         * The time that this row was last modified by a client (msecs since the epoch).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATE_MODIFIED = "modified";
+
     }
 
     interface BookmarkColumns {
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 6e5ed3a..ecd9fe9 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1267,10 +1267,10 @@
         }
 
         /**
-         * Mark a contact as having been contacted.  This updates the
-         * {@link #TIMES_CONTACTED} and {@link #LAST_TIME_CONTACTED} for the
-         * contact, plus the corresponding values of any associated raw
-         * contacts.
+         * Mark a contact as having been contacted. Updates two fields:
+         * {@link #TIMES_CONTACTED} and {@link #LAST_TIME_CONTACTED}. The
+         * TIMES_CONTACTED field is incremented by 1 and the LAST_TIME_CONTACTED
+         * field is populated with the current system time.
          *
          * @param resolver the ContentResolver to use
          * @param contactId the person who was contacted
@@ -1332,7 +1332,8 @@
 
         /**
          * A sub-directory of a single contact that contains all of the constituent raw contact
-         * {@link ContactsContract.Data} rows.
+         * {@link ContactsContract.Data} rows.  This directory can be used either
+         * with a {@link #CONTENT_URI} or {@link #CONTENT_LOOKUP_URI}.
          */
         public static final class Data implements BaseColumns, DataColumns {
             /**
@@ -1423,9 +1424,13 @@
          * </pre>
          *
          * </p>
+         * <p>
+         * This directory can be used either with a {@link #CONTENT_URI} or
+         * {@link #CONTENT_LOOKUP_URI}.
+         * </p>
          */
-        // TODO: add ContactOptionsColumns, ContactStatusColumns
-        public static final class AggregationSuggestions implements BaseColumns, ContactsColumns {
+        public static final class AggregationSuggestions implements BaseColumns, ContactsColumns,
+                ContactOptionsColumns, ContactStatusColumns {
             /**
              * No public constructor since this is a utility class
              */
@@ -1572,9 +1577,12 @@
          * <p>You should also consider using the convenience method
          * {@link ContactsContract.Contacts#openContactPhotoInputStream(ContentResolver, Uri)}
          * </p>
+         * <p>
+         * This directory can be used either with a {@link #CONTENT_URI} or
+         * {@link #CONTENT_LOOKUP_URI}.
+         * </p>
          */
-        // TODO: change DataColumns to DataColumnsWithJoins
-        public static final class Photo implements BaseColumns, DataColumns {
+        public static final class Photo implements BaseColumns, DataColumnsWithJoins {
             /**
              * no public constructor since this is a utility class
              */
@@ -1598,7 +1606,10 @@
          * Opens an InputStream for the contacts's default photo and returns the
          * photo as a byte stream. If there is not photo null will be returned.
          *
-         * @param contactUri the contact whose photo should be used
+         * @param contactUri the contact whose photo should be used. This can be used with
+         * either a {@link #CONTENT_URI} or a {@link #CONTENT_LOOKUP_URI} URI.
+         * </p>
+
          * @return an InputStream of the photo, or null if no photo is present
          */
         public static InputStream openContactPhotoInputStream(ContentResolver cr, Uri contactUri) {
@@ -2078,10 +2089,10 @@
         public static final int AGGREGATION_MODE_DEFAULT = 0;
 
         /**
-         * Do not use.
-         *
-         * TODO: deprecate in favor of {@link #AGGREGATION_MODE_DEFAULT}
+         * Aggregation mode: aggregate at the time the raw contact is inserted/updated.
+         * @deprecated Aggregation is synchronous, this historic value is a no-op
          */
+        @Deprecated
         public static final int AGGREGATION_MODE_IMMEDIATE = 1;
 
         /**
@@ -2149,9 +2160,7 @@
         /**
          * A sub-directory of a single raw contact that contains all of its
          * {@link ContactsContract.Data} rows. To access this directory
-         * append {@link Data#CONTENT_DIRECTORY} to the contact URI.
-         *
-         * TODO: deprecate in favor of {@link RawContacts.Entity}.
+         * append {@link Data#CONTENT_DIRECTORY} to the raw contact URI.
          */
         public static final class Data implements BaseColumns, DataColumns {
             /**
@@ -2170,7 +2179,7 @@
          * <p>
          * A sub-directory of a single raw contact that contains all of its
          * {@link ContactsContract.Data} rows. To access this directory append
-         * {@link #CONTENT_DIRECTORY} to the contact URI. See
+         * {@link RawContacts.Entity#CONTENT_DIRECTORY} to the raw contact URI. See
          * {@link RawContactsEntity} for a stand-alone table containing the same
          * data.
          * </p>
@@ -2182,10 +2191,10 @@
          * null.
          * </p>
          * <p>
-         * Entity reads all
-         * data for a raw contact in one transaction, to guarantee
-         * consistency.
-         * </p>
+         * Using Entity should be preferred to using two separate queries:
+         * RawContacts followed by Data. The reason is that Entity reads all
+         * data for a raw contact in one transaction, so there is no possibility
+         * of the data changing between the two queries.
          */
         public static final class Entity implements BaseColumns, DataColumns {
             /**
@@ -4256,7 +4265,7 @@
          * </tr>
          * <tr>
          * <td>String</td>
-         * <td>{@link #DATA}</td>
+         * <td>{@link #ADDRESS}</td>
          * <td>{@link #DATA1}</td>
          * <td>Email address itself.</td>
          * </tr>
diff --git a/core/java/android/provider/DrmStore.java b/core/java/android/provider/DrmStore.java
index 0a111a7..34f2f0d 100644
--- a/core/java/android/provider/DrmStore.java
+++ b/core/java/android/provider/DrmStore.java
@@ -143,11 +143,11 @@
             DrmRawContent content = new DrmRawContent(fis, (int) fis.available(),
                     DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING);
             String mimeType = content.getContentType();
+            long size = fis.getChannel().size();
 
             DrmRightsManager manager = manager = DrmRightsManager.getInstance();
             DrmRights rights = manager.queryRights(content);
             InputStream stream = content.getContentInputStream(rights);
-            long size = stream.available();
 
             Uri contentUri = null;
             if (mimeType.startsWith("audio/")) {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 60ccdaf..9b2b090 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -260,31 +260,31 @@
      * Media provider interface used by MTP implementation.
      * @hide
      */
-    public static final class MtpObjects {
+    public static final class Files {
 
         public static Uri getContentUri(String volumeName) {
             return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                    "/object");
+                    "/file");
         }
 
         public static final Uri getContentUri(String volumeName,
-                long objectId) {
+                long fileId) {
             return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
-                    + "/object/" + objectId);
+                    + "/file/" + fileId);
         }
 
         // used for MTP GetObjectReferences and SetObjectReferences
         public static final Uri getReferencesUri(String volumeName,
-                long objectId) {
+                long fileId) {
             return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
-                    + "/object/" + objectId + "/references");
+                    + "/file/" + fileId + "/references");
         }
 
         /**
          * Fields for master table for all media files.
          * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
          */
-        public interface ObjectColumns extends MediaColumns {
+        public interface FileColumns extends MediaColumns {
             /**
              * The MTP format code of the file
              * <P>Type: INTEGER</P>
@@ -298,14 +298,14 @@
             public static final String PARENT = "parent";
 
             /**
-             * Identifier for the media table containing the object.
+             * Identifier for the media table containing the file.
              * Used internally by MediaProvider
              * <P>Type: INTEGER</P>
              */
             public static final String MEDIA_TABLE = "media_table";
 
             /**
-             * The ID of the object in its media table.
+             * The ID of the file in its media table.
              * <P>Type: INTEGER</P>
              */
             public static final String MEDIA_ID = "media_id";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 158558e..567ee93 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2421,6 +2421,14 @@
             "lock_pattern_tactile_feedback_enabled";
 
         /**
+         * This preference allows the device to be locked given time after screen goes off,
+         * subject to current DeviceAdmin policy limits.
+         * @hide
+         */
+        public static final String LOCK_SCREEN_LOCK_AFTER_TIMEOUT = "lock_screen_lock_after_timeout";
+
+
+        /**
          * Whether assisted GPS should be enabled or not.
          * @hide
          */
@@ -2997,31 +3005,31 @@
         public static final String WTF_IS_FATAL = "wtf_is_fatal";
 
         /**
-         * Maximum age of entries kept by {@link android.os.IDropBox}.
+         * Maximum age of entries kept by {@link com.android.internal.os.IDropBoxManagerService}.
          * @hide
          */
         public static final String DROPBOX_AGE_SECONDS =
                 "dropbox_age_seconds";
         /**
-         * Maximum number of entry files which {@link android.os.IDropBox} will keep around.
+         * Maximum number of entry files which {@link com.android.internal.os.IDropBoxManagerService} will keep around.
          * @hide
          */
         public static final String DROPBOX_MAX_FILES =
                 "dropbox_max_files";
         /**
-         * Maximum amount of disk space used by {@link android.os.IDropBox} no matter what.
+         * Maximum amount of disk space used by {@link com.android.internal.os.IDropBoxManagerService} no matter what.
          * @hide
          */
         public static final String DROPBOX_QUOTA_KB =
                 "dropbox_quota_kb";
         /**
-         * Percent of free disk (excluding reserve) which {@link android.os.IDropBox} will use.
+         * Percent of free disk (excluding reserve) which {@link com.android.internal.os.IDropBoxManagerService} will use.
          * @hide
          */
         public static final String DROPBOX_QUOTA_PERCENT =
                 "dropbox_quota_percent";
         /**
-         * Percent of total disk which {@link android.os.IDropBox} will never dip into.
+         * Percent of total disk which {@link com.android.internal.os.IDropBoxManagerService} will never dip into.
          * @hide
          */
         public static final String DROPBOX_RESERVE_PERCENT =
@@ -3081,6 +3089,15 @@
                 "sys_storage_threshold_percentage";
 
         /**
+         * Minimum bytes of free storage on the device before the data
+         * partition is considered full. By default, 1 MB is reserved
+         * to avoid system-wide SQLite disk full exceptions.
+         * @hide
+         */
+        public static final String SYS_STORAGE_FULL_THRESHOLD_BYTES =
+                "sys_storage_full_threshold_bytes";
+
+        /**
          * The interval in milliseconds after which Wi-Fi is considered idle.
          * When idle, it is possible for the device to be switched from Wi-Fi to
          * the mobile data network.
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 3861ef5..48a2b72 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -644,7 +644,8 @@
              } else {
                  Log.i(TAG, "Rejecting incoming HID connection from " + address);
              }
-        } else if (BluetoothUuid.isBnep(uuid) || BluetoothUuid.isNap(uuid)){
+        } else if (BluetoothUuid.isBnep(uuid) || BluetoothUuid.isNap(uuid) &&
+                mBluetoothService.isTetheringOn()){
             authorized = true;
         } else {
             Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
@@ -760,6 +761,16 @@
         }
     }
 
+    private void onNetworkDeviceDisconnected(String address) {
+        BluetoothDevice device = mAdapter.getRemoteDevice(address);
+        mBluetoothService.handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED);
+    }
+
+    private void onNetworkDeviceConnected(String address, int destUuid) {
+        BluetoothDevice device = mAdapter.getRemoteDevice(address);
+        mBluetoothService.handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTED);
+    }
+
     private void onRestartRequired() {
         if (mBluetoothService.isEnabled()) {
             Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index fa5f156..7252736 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -352,7 +352,7 @@
         }
         setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
         mHandler.removeMessages(MESSAGE_REGISTER_SDP_RECORDS);
-        setBluetoothTethering(false, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
+        setBluetoothTetheringNative(false, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
 
         // Allow 3 seconds for profiles to gracefully disconnect
         // TODO: Introduce a callback mechanism so that each profile can notify
@@ -576,8 +576,12 @@
                 mBondState.readAutoPairingData();
                 mBondState.loadBondState();
                 initProfileState();
+
+                //Register SDP records.
                 mHandler.sendMessageDelayed(
                         mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000);
+                setBluetoothTetheringNative(true, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
+
 
                 // Log bluetooth on to battery stats.
                 long ident = Binder.clearCallingIdentity();
@@ -1258,14 +1262,12 @@
 
     private BroadcastReceiver mTetheringReceiver = null;
 
-    public synchronized void setBluetoothTethering(boolean value, 
-            final String uuid, final String bridge) {
-        mTetheringOn = value;
+    public synchronized void setBluetoothTethering(boolean value) {
         if (!value) {
             disconnectPan();
         }
 
-        if (getBluetoothState() != BluetoothAdapter.STATE_ON && mTetheringOn) {
+        if (getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
             IntentFilter filter = new IntentFilter();
             filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
             mTetheringReceiver = new BroadcastReceiver() {
@@ -1273,14 +1275,14 @@
                 public synchronized void onReceive(Context context, Intent intent) {
                     if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
                             == BluetoothAdapter.STATE_ON) {
-                        setBluetoothTethering(true, uuid, bridge);
+                        mTetheringOn = true;
                         mContext.unregisterReceiver(mTetheringReceiver);
                     }
                 }
             };
             mContext.registerReceiver(mTetheringReceiver, filter);
         } else {
-            setBluetoothTetheringNative(value, uuid, bridge);
+            mTetheringOn = value;
         }
     }
 
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 89b3cba..a95dad7 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -168,6 +168,12 @@
     public static final int FORMAT_CAP_NOON = 0x00400;
     public static final int FORMAT_NO_MIDNIGHT = 0x00800;
     public static final int FORMAT_CAP_MIDNIGHT = 0x01000;
+    /**
+     * @deprecated Use
+     * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}
+     * and pass in {@link Time#TIMEZONE_UTC Time.TIMEZONE_UTC} for the timeZone instead.
+     */
+    @Deprecated
     public static final int FORMAT_UTC = 0x02000;
     public static final int FORMAT_ABBREV_TIME = 0x04000;
     public static final int FORMAT_ABBREV_WEEKDAY = 0x08000;
@@ -964,12 +970,12 @@
      * {@link java.util.Formatter} instance and use the version of
      * {@link #formatDateRange(Context, long, long, int) formatDateRange}
      * that takes a {@link java.util.Formatter}.
-     * 
+     *
      * @param context the context is required only if the time is shown
      * @param startMillis the start time in UTC milliseconds
      * @param endMillis the end time in UTC milliseconds
      * @param flags a bit mask of options See
-     * {@link #formatDateRange(Context, long, long, int) formatDateRange}
+     * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}
      * @return a string containing the formatted date/time range.
      */
     public static String formatDateRange(Context context, long startMillis,
@@ -980,6 +986,29 @@
 
     /**
      * Formats a date or a time range according to the local conventions.
+     * <p>
+     * Note that this is a convenience method for formatting the date or
+     * time range in the local time zone. If you want to specify the time
+     * zone please use
+     * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}.
+     *
+     * @param context the context is required only if the time is shown
+     * @param formatter the Formatter used for formatting the date range.
+     * Note: be sure to call setLength(0) on StringBuilder passed to
+     * the Formatter constructor unless you want the results to accumulate.
+     * @param startMillis the start time in UTC milliseconds
+     * @param endMillis the end time in UTC milliseconds
+     * @param flags a bit mask of options See
+     * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}
+     * @return a string containing the formatted date/time range.
+     */
+    public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,
+            long endMillis, int flags) {
+        return formatDateRange(context, formatter, startMillis, endMillis, flags, null);
+    }
+
+    /**
+     * Formats a date or a time range according to the local conventions.
      * 
      * <p>
      * Example output strings (date formats in these examples are shown using
@@ -1094,8 +1123,9 @@
      * FORMAT_24HOUR takes precedence.
      * 
      * <p>
-     * If FORMAT_UTC is set, then the UTC timezone is used for the start
-     * and end milliseconds.
+     * If FORMAT_UTC is set, then the UTC time zone is used for the start
+     * and end milliseconds unless a time zone is specified. If a time zone
+     * is specified it will be used regardless of the FORMAT_UTC flag.
      * 
      * <p>
      * If FORMAT_ABBREV_TIME is set and 12-hour time format is used, then the
@@ -1127,11 +1157,13 @@
      * @param startMillis the start time in UTC milliseconds
      * @param endMillis the end time in UTC milliseconds
      * @param flags a bit mask of options
-     *   
+     * @param timeZone the time zone to compute the string in. Use null for local
+     * or if the FORMAT_UTC flag is being used.
+     *  
      * @return the formatter with the formatted date/time range appended to the string buffer.
      */
     public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,
-            long endMillis, int flags) {
+            long endMillis, int flags, String timeZone) {
         Resources res = Resources.getSystem();
         boolean showTime = (flags & FORMAT_SHOW_TIME) != 0;
         boolean showWeekDay = (flags & FORMAT_SHOW_WEEKDAY) != 0;
@@ -1148,7 +1180,14 @@
         // computation below that'd otherwise be thrown out.
         boolean isInstant = (startMillis == endMillis);
 
-        Time startDate = useUTC ? new Time(Time.TIMEZONE_UTC) : new Time();
+        Time startDate;
+        if (timeZone != null) {
+            startDate = new Time(timeZone);
+        } else if (useUTC) {
+            startDate = new Time(Time.TIMEZONE_UTC);
+        } else {
+            startDate = new Time();
+        }
         startDate.set(startMillis);
 
         Time endDate;
@@ -1157,7 +1196,13 @@
             endDate = startDate;
             dayDistance = 0;
         } else {
-            endDate = useUTC ? new Time(Time.TIMEZONE_UTC) : new Time();
+            if (timeZone != null) {
+                endDate = new Time(timeZone);
+            } else if (useUTC) {
+                endDate = new Time(Time.TIMEZONE_UTC);
+            } else {
+                endDate = new Time();
+            }
             endDate.set(endMillis);
             int startJulianDay = Time.getJulianDay(startMillis, startDate.gmtoff);
             int endJulianDay = Time.getJulianDay(endMillis, endDate.gmtoff);
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 3fe14f9..0408673 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -34,7 +34,7 @@
      * An optional controller for the cursor.
      * Use {@link #setCursorController(CursorController)} to set this field.
      */
-    protected CursorController mCursorController;
+    private CursorController mCursorController;
 
     private boolean isCap(Spannable buffer) {
         return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
@@ -302,7 +302,17 @@
     /**
      * Defines the cursor controller.
      *
-     * When set, this object can be used to handle events, that can be translated in cursor updates.
+     * When set, this object can be used to handle touch events, that can be translated into cursor
+     * updates.
+     *
+     * {@link MotionEvent#ACTION_MOVE} events will call back the 
+     * {@link CursorController#updatePosition(int, int)} controller's method, passing the current
+     * finger coordinates (offset by {@link CursorController#getOffsetX()} and
+     * {@link CursorController#getOffsetY()}) as parameters. 
+     *
+     * When the gesture is finished (on a {@link MotionEvent#ACTION_UP} or
+     * {@link MotionEvent#ACTION_CANCEL} event), the controller is reset to null.
+     *
      * @param cursorController A cursor controller implementation
      */
     public void setCursorController(CursorController cursorController) {
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index b01a71d..60ca384 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -132,20 +132,76 @@
         return ZoneInfoDB.getVersion();
     }
 
+    /** @hide Field length that can hold 999 days of time */
+    public static final int HUNDRED_DAY_FIELD_LEN = 19;
+    
     private static final int SECONDS_PER_MINUTE = 60;
     private static final int SECONDS_PER_HOUR = 60 * 60;
     private static final int SECONDS_PER_DAY = 24 * 60 * 60;
 
-    /** @hide Just for debugging; not internationalized. */
-    public static void formatDuration(long duration, StringBuilder builder) {
-        if (duration == 0) {
-            builder.append("0");
-            return;
+    private static final Object sFormatSync = new Object();
+    private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5];
+    
+    static private int accumField(int amt, int suffix, boolean always, int zeropad) {
+        if (amt > 99 || (always && zeropad >= 3)) {
+            return 3+suffix;
         }
+        if (amt > 9 || (always && zeropad >= 2)) {
+            return 2+suffix;
+        }
+        if (always || amt > 0) {
+            return 1+suffix;
+        }
+        return 0;
+    }
+    
+    static private int printField(char[] formatStr, int amt, char suffix, int pos,
+            boolean always, int zeropad) {
+        if (always || amt > 0) {
+            if ((always && zeropad >= 3) || amt > 99) {
+                int dig = amt/100;
+                formatStr[pos] = (char)(dig + '0');
+                pos++;
+                always = true;
+                amt -= (dig*100);
+            }
+            if ((always && zeropad >= 2) || amt > 9) {
+                int dig = amt/10;
+                formatStr[pos] = (char)(dig + '0');
+                pos++;
+                always = true;
+                amt -= (dig*10);
+            }
+            formatStr[pos] = (char)(amt + '0');
+            pos++;
+            formatStr[pos] = suffix;
+            pos++;
+        }
+        return pos;
+    }
+    
+    private static int formatDurationLocked(long duration, int fieldLen) {
+        if (sFormatStr.length < fieldLen) {
+            sFormatStr = new char[fieldLen];
+        }
+        
+        char[] formatStr = sFormatStr;
+        
+        if (duration == 0) {
+            int pos = 0;
+            fieldLen -= 1;
+            while (pos < fieldLen) {
+                formatStr[pos] = ' ';
+            }
+            formatStr[pos] = '0';
+            return pos+1;
+        }
+        
+        char prefix;
         if (duration > 0) {
-            builder.append("+");
+            prefix = '+';
         } else {
-            builder.append("-");
+            prefix = '-';
             duration = -duration;
         }
 
@@ -166,93 +222,62 @@
             seconds -= minutes * SECONDS_PER_MINUTE;
         }
 
-        boolean doall = false;
-        if (days > 0) {
-            builder.append(days);
-            builder.append('d');
-            doall = true;
+        int pos = 0;
+        
+        if (fieldLen != 0) {
+            int myLen = accumField(days, 1, false, 0);
+            myLen += accumField(hours, 1, myLen > 0, 2);
+            myLen += accumField(minutes, 1, myLen > 0, 2);
+            myLen += accumField(seconds, 1, myLen > 0, 2);
+            myLen += accumField(millis, 2, true, myLen > 0 ? 3 : 0) + 1;
+            while (myLen < fieldLen) {
+                formatStr[pos] = ' ';
+                pos++;
+                myLen++;
+            }
         }
-        if (doall || hours > 0) {
-            builder.append(hours);
-            builder.append('h');
-            doall = true;
+        
+        formatStr[pos] = prefix;
+        pos++;
+        
+        int start = pos;
+        boolean zeropad = fieldLen != 0;
+        pos = printField(formatStr, days, 'd', pos, false, 0);
+        pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0);
+        pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0);
+        pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0);
+        pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0);
+        formatStr[pos] = 's';
+        return pos + 1;
+    }
+    
+    /** @hide Just for debugging; not internationalized. */
+    public static void formatDuration(long duration, StringBuilder builder) {
+        synchronized (sFormatSync) {
+            int len = formatDurationLocked(duration, 0);
+            builder.append(sFormatStr, 0, len);
         }
-        if (doall || minutes > 0) {
-            builder.append(minutes);
-            builder.append('m');
-            doall = true;
+    }
+
+    /** @hide Just for debugging; not internationalized. */
+    public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
+        synchronized (sFormatSync) {
+            int len = formatDurationLocked(duration, fieldLen);
+            pw.print(new String(sFormatStr, 0, len));
         }
-        if (doall || seconds > 0) {
-            builder.append(seconds);
-            builder.append('s');
-            doall = true;
-        }
-        builder.append(millis);
-        builder.append("ms");
     }
 
     /** @hide Just for debugging; not internationalized. */
     public static void formatDuration(long duration, PrintWriter pw) {
-        if (duration == 0) {
-            pw.print("0");
-            return;
-        }
-        if (duration > 0) {
-            pw.print("+");
-        } else {
-            pw.print("-");
-            duration = -duration;
-        }
-
-        int millis = (int)(duration%1000);
-        int seconds = (int) Math.floor(duration / 1000);
-        int days = 0, hours = 0, minutes = 0;
-
-        if (seconds > SECONDS_PER_DAY) {
-            days = seconds / SECONDS_PER_DAY;
-            seconds -= days * SECONDS_PER_DAY;
-        }
-        if (seconds > SECONDS_PER_HOUR) {
-            hours = seconds / SECONDS_PER_HOUR;
-            seconds -= hours * SECONDS_PER_HOUR;
-        }
-        if (seconds > SECONDS_PER_MINUTE) {
-            minutes = seconds / SECONDS_PER_MINUTE;
-            seconds -= minutes * SECONDS_PER_MINUTE;
-        }
-
-        boolean doall = false;
-        if (days > 0) {
-            pw.print(days);
-            pw.print('d');
-            doall = true;
-        }
-        if (doall || hours > 0) {
-            pw.print(hours);
-            pw.print('h');
-            doall = true;
-        }
-        if (doall || minutes > 0) {
-            pw.print(minutes);
-            pw.print('m');
-            doall = true;
-        }
-        if (doall || seconds > 0) {
-            pw.print(seconds);
-            pw.print('s');
-            doall = true;
-        }
-        pw.print(millis);
-        pw.print("ms");
+        formatDuration(duration, pw, 0);
     }
-
-
+    
     /** @hide Just for debugging; not internationalized. */
     public static void formatDuration(long time, long now, PrintWriter pw) {
         if (time == 0) {
             pw.print("--");
             return;
         }
-        formatDuration(time-now, pw);
+        formatDuration(time-now, pw, 0);
     }
 }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 42e3641..5d85076a 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -37,10 +37,27 @@
 public abstract class HardwareRenderer {
     private static final String LOG_TAG = "HardwareRenderer";
 
+    /**
+     * A process can set this flag to false to prevent the use of hardware
+     * rendering.
+     * 
+     * @hide
+     */
+    public static boolean sRendererDisabled = false;
+
     private boolean mEnabled;
     private boolean mRequested = true;
 
     /**
+     * Indicates that the current process cannot use hardware rendering.
+     * 
+     * @hide
+     */
+    public static void disable() {
+        sRendererDisabled = true;
+    }
+
+    /**
      * Indicates whether hardware acceleration is available under any form for
      * the view hierarchy.
      * 
@@ -377,7 +394,6 @@
          * @param glVersion
          */
         EglConfigChooser getConfigChooser(int glVersion) {
-            // TODO: Check for mTranslucent here, which means at least 2 EGL contexts
             return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0);
         }
 
@@ -546,7 +562,6 @@
 
         @Override
         GLES20Canvas createCanvas() {
-            // TODO: Pass mTranslucent instead of true
             return mGlCanvas = new GLES20Canvas(mGl, true);
         }
 
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 74318ba..78b9b5d 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -30,6 +30,7 @@
  */
 public final class MotionEvent extends InputEvent implements Parcelable {
     private static final long MS_PER_NS = 1000000;
+    private static final boolean TRACK_RECYCLED_LOCATION = false;
     
     /**
      * Bit mask of the parts of the action code that are the action itself.
@@ -155,7 +156,17 @@
     @Deprecated
     public static final int ACTION_POINTER_ID_SHIFT = 8;
     
-    private static final boolean TRACK_RECYCLED_LOCATION = false;
+    /**
+     * This flag indicates that the window that received this motion event is partly
+     * or wholly obscured by another visible window above it.  This flag is set to true
+     * even if the event did not directly pass through the obscured area.
+     * A security sensitive application can check this flag to identify situations in which
+     * a malicious application may have covered up part of its content for the purpose
+     * of misleading the user or hijacking touches.  An appropriate response might be
+     * to drop the suspect touches or to take additional precautions to confirm the user's
+     * actual intent.
+     */
+    public static final int FLAG_WINDOW_IS_OBSCURED = 0x1;
 
     /**
      * Flag indicating the motion event intersected the top edge of the screen.
@@ -251,6 +262,7 @@
     private float mYPrecision;
     private int mEdgeFlags;
     private int mMetaState;
+    private int mFlags;
     
     private int mNumPointers;
     private int mNumSamples;
@@ -338,20 +350,22 @@
      * @param deviceId The id for the device that this event came from.  An id of
      * zero indicates that the event didn't come from a physical device; other
      * numbers are arbitrary and you shouldn't depend on the values.
-     * @param edgeFlags A bitfield indicating which edges, if any, where touched by this
+     * @param edgeFlags A bitfield indicating which edges, if any, were touched by this
      * MotionEvent.
      * @param source The source of this event.
+     * @param flags The motion event flags.
      */
     static public MotionEvent obtain(long downTime, long eventTime,
             int action, int pointers, int[] pointerIds, PointerCoords[] pointerCoords,
             int metaState, float xPrecision, float yPrecision, int deviceId,
-            int edgeFlags, int source) {
+            int edgeFlags, int source, int flags) {
         MotionEvent ev = obtain(pointers, 1);
         ev.mDeviceId = deviceId;
         ev.mSource = source;
         ev.mEdgeFlags = edgeFlags;
         ev.mDownTimeNano = downTime * MS_PER_NS;
         ev.mAction = action;
+        ev.mFlags = flags;
         ev.mMetaState = metaState;
         ev.mXOffset = 0;
         ev.mYOffset = 0;
@@ -401,7 +415,7 @@
      * @param deviceId The id for the device that this event came from.  An id of
      * zero indicates that the event didn't come from a physical device; other
      * numbers are arbitrary and you shouldn't depend on the values.
-     * @param edgeFlags A bitfield indicating which edges, if any, where touched by this
+     * @param edgeFlags A bitfield indicating which edges, if any, were touched by this
      * MotionEvent.
      */
     static public MotionEvent obtain(long downTime, long eventTime, int action,
@@ -413,6 +427,7 @@
         ev.mEdgeFlags = edgeFlags;
         ev.mDownTimeNano = downTime * MS_PER_NS;
         ev.mAction = action;
+        ev.mFlags = 0;
         ev.mMetaState = metaState;
         ev.mXOffset = 0;
         ev.mYOffset = 0;
@@ -462,7 +477,7 @@
      * @param deviceId The id for the device that this event came from.  An id of
      * zero indicates that the event didn't come from a physical device; other
      * numbers are arbitrary and you shouldn't depend on the values.
-     * @param edgeFlags A bitfield indicating which edges, if any, where touched by this
+     * @param edgeFlags A bitfield indicating which edges, if any, were touched by this
      * MotionEvent.
      * 
      * @deprecated Use {@link #obtain(long, long, int, float, float, float, float, int, float, float, int, int)}
@@ -509,6 +524,7 @@
         ev.mEdgeFlags = o.mEdgeFlags;
         ev.mDownTimeNano = o.mDownTimeNano;
         ev.mAction = o.mAction;
+        ev.mFlags = o.mFlags;
         ev.mMetaState = o.mMetaState;
         ev.mXOffset = o.mXOffset;
         ev.mYOffset = o.mYOffset;
@@ -540,6 +556,7 @@
         ev.mEdgeFlags = o.mEdgeFlags;
         ev.mDownTimeNano = o.mDownTimeNano;
         ev.mAction = o.mAction;
+        o.mFlags = o.mFlags;
         ev.mMetaState = o.mMetaState;
         ev.mXOffset = o.mXOffset;
         ev.mYOffset = o.mYOffset;
@@ -651,6 +668,15 @@
     }
 
     /**
+     * Gets the motion event flags.
+     *
+     * @see #FLAG_WINDOW_IS_OBSCURED
+     */
+    public final int getFlags() {
+        return mFlags;
+    }
+
+    /**
      * Returns the time (in ms) when the user originally pressed down to start
      * a stream of position events.
      */
@@ -1285,7 +1311,7 @@
 
 
     /**
-     * Sets the bitfield indicating which edges, if any, where touched by this
+     * Sets the bitfield indicating which edges, if any, were touched by this
      * MotionEvent.
      *
      * @see #getEdgeFlags()
@@ -1480,6 +1506,7 @@
         ev.mYPrecision = in.readFloat();
         ev.mEdgeFlags = in.readInt();
         ev.mMetaState = in.readInt();
+        ev.mFlags = in.readInt();
         
         final int[] pointerIdentifiers = ev.mPointerIdentifiers;
         for (int i = 0; i < NP; i++) {
@@ -1521,6 +1548,7 @@
         out.writeFloat(mYPrecision);
         out.writeInt(mEdgeFlags);
         out.writeInt(mMetaState);
+        out.writeInt(mFlags);
         
         final int[] pointerIdentifiers = mPointerIdentifiers;
         for (int i = 0; i < NP; i++) {
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 0999598..17b5dd7 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -25,7 +25,7 @@
  * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
  * callback will notify users when a particular gesture event has occurred.
  * This class should only be used with {@link MotionEvent}s reported via touch.
- * 
+ *
  * To use this class:
  * <ul>
  *  <li>Create an instance of the {@code ScaleGestureDetector} for your
@@ -41,7 +41,7 @@
      * If you want to listen for all the different gestures then implement
      * this interface. If you only want to listen for a subset it might
      * be easier to extend {@link SimpleOnScaleGestureListener}.
-     * 
+     *
      * An application will receive events in the following order:
      * <ul>
      *  <li>One {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)}
@@ -53,7 +53,7 @@
         /**
          * Responds to scaling events for a gesture in progress.
          * Reported by pointer motion.
-         * 
+         *
          * @param detector The detector reporting the event - use this to
          *          retrieve extended info about event state.
          * @return Whether or not the detector should consider this event
@@ -68,7 +68,7 @@
         /**
          * Responds to the beginning of a scaling gesture. Reported by
          * new pointers going down.
-         * 
+         *
          * @param detector The detector reporting the event - use this to
          *          retrieve extended info about event state.
          * @return Whether or not the detector should continue recognizing
@@ -82,17 +82,17 @@
         /**
          * Responds to the end of a scale gesture. Reported by existing
          * pointers going up.
-         * 
+         *
          * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
          * and {@link ScaleGestureDetector#getFocusY()} will return the location
          * of the pointer remaining on the screen.
-         * 
+         *
          * @param detector The detector reporting the event - use this to
          *          retrieve extended info about event state.
          */
         public void onScaleEnd(ScaleGestureDetector detector);
     }
-    
+
     /**
      * A convenience class to extend when you only want to listen for a subset
      * of scaling-related events. This implements all methods in
@@ -101,7 +101,7 @@
      * {@code false} so that a subclass can retrieve the accumulated scale
      * factor in an overridden onScaleEnd.
      * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} returns
-     * {@code true}. 
+     * {@code true}.
      */
     public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {
 
@@ -148,7 +148,7 @@
     private float mCurrPressure;
     private float mPrevPressure;
     private long mTimeDelta;
-    
+
     private final float mEdgeSlop;
     private float mRightSlopEdge;
     private float mBottomSlopEdge;
@@ -217,7 +217,7 @@
                 }
             }
             break;
-            
+
             case MotionEvent.ACTION_MOVE:
                 if (mSloppyGesture) {
                     // Initiate sloppy gestures if we've moved outside of the slop area.
@@ -249,7 +249,7 @@
                     }
                 }
                 break;
-                
+
             case MotionEvent.ACTION_POINTER_UP:
                 if (mSloppyGesture) {
                     // Set focus point to the remaining finger
@@ -307,17 +307,17 @@
         }
         return handled;
     }
-    
+
     /**
-     * MotionEvent has no getRawX(int) method; simulate it pending future API approval. 
+     * MotionEvent has no getRawX(int) method; simulate it pending future API approval.
      */
     private static float getRawX(MotionEvent event, int pointerIndex) {
         float offset = event.getRawX() - event.getX();
         return event.getX(pointerIndex) + offset;
     }
-    
+
     /**
-     * MotionEvent has no getRawY(int) method; simulate it pending future API approval. 
+     * MotionEvent has no getRawY(int) method; simulate it pending future API approval.
      */
     private static float getRawY(MotionEvent event, int pointerIndex) {
         float offset = event.getRawY() - event.getY();
@@ -390,7 +390,7 @@
      * remaining pointer on the screen.
      * If {@link #isInProgress()} would return false, the result of this
      * function is undefined.
-     * 
+     *
      * @return X coordinate of the focal point in pixels.
      */
     public float getFocusX() {
@@ -405,7 +405,7 @@
      * remaining pointer on the screen.
      * If {@link #isInProgress()} would return false, the result of this
      * function is undefined.
-     * 
+     *
      * @return Y coordinate of the focal point in pixels.
      */
     public float getFocusY() {
@@ -415,7 +415,7 @@
     /**
      * Return the current distance between the two pointers forming the
      * gesture in progress.
-     * 
+     *
      * @return Distance between pointers in pixels.
      */
     public float getCurrentSpan() {
@@ -428,9 +428,29 @@
     }
 
     /**
+     * Return the current x distance between the two pointers forming the
+     * gesture in progress.
+     *
+     * @return Distance between pointers in pixels.
+     */
+    public float getCurrentSpanX() {
+        return mCurrFingerDiffX;
+    }
+
+    /**
+     * Return the current y distance between the two pointers forming the
+     * gesture in progress.
+     *
+     * @return Distance between pointers in pixels.
+     */
+    public float getCurrentSpanY() {
+        return mCurrFingerDiffY;
+    }
+
+    /**
      * Return the previous distance between the two pointers forming the
      * gesture in progress.
-     * 
+     *
      * @return Previous distance between pointers in pixels.
      */
     public float getPreviousSpan() {
@@ -443,10 +463,30 @@
     }
 
     /**
+     * Return the previous x distance between the two pointers forming the
+     * gesture in progress.
+     *
+     * @return Previous distance between pointers in pixels.
+     */
+    public float getPreviousSpanX() {
+        return mPrevFingerDiffX;
+    }
+
+    /**
+     * Return the previous y distance between the two pointers forming the
+     * gesture in progress.
+     *
+     * @return Previous distance between pointers in pixels.
+     */
+    public float getPreviousSpanY() {
+        return mPrevFingerDiffY;
+    }
+
+    /**
      * Return the scaling factor from the previous scale event to the current
      * event. This value is defined as
      * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}).
-     * 
+     *
      * @return The current scaling factor.
      */
     public float getScaleFactor() {
@@ -455,20 +495,20 @@
         }
         return mScaleFactor;
     }
-    
+
     /**
      * Return the time difference in milliseconds between the previous
      * accepted scaling event and the current scaling event.
-     * 
+     *
      * @return Time difference since the last scaling event in milliseconds.
      */
     public long getTimeDelta() {
         return mTimeDelta;
     }
-    
+
     /**
      * Return the event time of the current event being processed.
-     * 
+     *
      * @return Current event time in milliseconds.
      */
     public long getEventTime() {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 616128d..5d258967 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -58,7 +58,6 @@
 import android.util.Pools;
 import android.util.SparseArray;
 import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
@@ -73,6 +72,7 @@
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.WeakHashMap;
 
 /**
@@ -545,6 +545,28 @@
  * take care of redrawing the appropriate views until the animation completes.
  * </p>
  *
+ * <a name="Security"></a>
+ * <h3>Security</h3>
+ * <p>
+ * Sometimes it is essential that an application be able to verify that an action
+ * is being performed with the full knowledge and consent of the user, such as
+ * granting a permission request, making a purchase or clicking on an advertisement.
+ * Unfortunately, a malicious application could try to spoof the user into
+ * performing these actions, unaware, by concealing the intended purpose of the view.
+ * As a remedy, the framework offers a touch filtering mechanism that can be used to
+ * improve the security of views that provide access to sensitive functionality.
+ * </p><p>
+ * To enable touch filtering, call {@link #setFilterTouchesWhenObscured} or set the
+ * andoird:filterTouchesWhenObscured attribute to true.  When enabled, the framework
+ * will discard touches that are received whenever the view's window is obscured by
+ * another visible window.  As a result, the view will not receive touches whenever a
+ * toast, dialog or other window appears above the view's window.
+ * </p><p>
+ * For more fine-grained control over security, consider overriding the
+ * {@link #onFilterTouchEventForSecurity} method to implement your own security policy.
+ * See also {@link MotionEvent#FLAG_WINDOW_IS_OBSCURED}.
+ * </p>
+ *
  * @attr ref android.R.styleable#View_background
  * @attr ref android.R.styleable#View_clickable
  * @attr ref android.R.styleable#View_contentDescription
@@ -553,6 +575,7 @@
  * @attr ref android.R.styleable#View_id
  * @attr ref android.R.styleable#View_fadingEdge
  * @attr ref android.R.styleable#View_fadingEdgeLength
+ * @attr ref android.R.styleable#View_filterTouchesWhenObscured
  * @attr ref android.R.styleable#View_fitsSystemWindows
  * @attr ref android.R.styleable#View_isScrollContainer
  * @attr ref android.R.styleable#View_focusable
@@ -714,7 +737,14 @@
      */
     static final int SCROLLBARS_MASK = 0x00000300;
 
-    // note 0x00000400 and 0x00000800 are now available for next flags...
+    /**
+     * Indicates that the view should filter touches when its window is obscured.
+     * Refer to the class comments for more information about this security feature.
+     * {@hide}
+     */
+    static final int FILTER_TOUCHES_WHEN_OBSCURED = 0x00000400;
+
+    // note flag value 0x00000800 is now available for next flags...
 
     /**
      * <p>This view doesn't show fading edges.</p>
@@ -1838,6 +1868,11 @@
     protected OnFocusChangeListener mOnFocusChangeListener;
 
     /**
+     * Listeners for layout change events.
+     */
+    private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
+
+    /**
      * Listener used to dispatch click events.
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
@@ -1950,9 +1985,6 @@
      */
     private int mTouchSlop;
 
-    // Used for debug only
-    static long sInstanceCount = 0;
-
     /**
      * Simple constructor to use when creating a view from code.
      *
@@ -1963,8 +1995,6 @@
         mContext = context;
         mResources = context != null ? context.getResources() : null;
         mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
-        // Used for debug only
-        //++sInstanceCount;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
     }
 
@@ -2171,6 +2201,12 @@
                         viewFlagMasks |= KEEP_SCREEN_ON;
                     }
                     break;
+                case R.styleable.View_filterTouchesWhenObscured:
+                    if (a.getBoolean(attr, false)) {
+                        viewFlagValues |= FILTER_TOUCHES_WHEN_OBSCURED;
+                        viewFlagMasks |= FILTER_TOUCHES_WHEN_OBSCURED;
+                    }
+                    break;
                 case R.styleable.View_nextFocusLeft:
                     mNextFocusLeftId = a.getResourceId(attr, View.NO_ID);
                     break;
@@ -2281,15 +2317,6 @@
     View() {
     }
 
-    // Used for debug only
-    /*
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        --sInstanceCount;
-    }
-    */
-
     /**
      * <p>
      * Initializes the fading edges from a given set of styled attributes. This
@@ -2496,6 +2523,39 @@
     }
 
     /**
+     * Add a listener that will be called when the bounds of the view change due to
+     * layout processing.
+     *
+     * @param listener The listener that will be called when layout bounds change.
+     */
+    public void addOnLayoutChangeListener(OnLayoutChangeListener listener) {
+        if (mOnLayoutChangeListeners == null) {
+            mOnLayoutChangeListeners = new ArrayList<OnLayoutChangeListener>();
+        }
+        mOnLayoutChangeListeners.add(listener);
+    }
+
+    /**
+     * Remove a listener for layout changes.
+     *
+     * @param listener The listener for layout bounds change.
+     */
+    public void removeOnLayoutChangeListener(OnLayoutChangeListener listener) {
+        if (mOnLayoutChangeListeners == null) {
+            return;
+        }
+        mOnLayoutChangeListeners.remove(listener);
+    }
+
+    /**
+     * Gets the current list of listeners for layout changes.
+     * @return
+     */
+    public List<OnLayoutChangeListener> getOnLayoutChangeListeners() {
+        return mOnLayoutChangeListeners;
+    }
+
+    /**
      * Returns the focus-change callback registered for this view.
      *
      * @return The callback, or null if one is not registered.
@@ -3515,6 +3575,35 @@
         setFlags(enabled ? 0 : SAVE_DISABLED, SAVE_DISABLED_MASK);
     }
 
+    /**
+     * Gets whether the framework should discard touches when the view's
+     * window is obscured by another visible window.
+     * Refer to the {@link View} security documentation for more details.
+     *
+     * @return True if touch filtering is enabled.
+     *
+     * @see #setFilterTouchesWhenObscured(boolean)
+     * @attr ref android.R.styleable#View_filterTouchesWhenObscured
+     */
+    @ViewDebug.ExportedProperty
+    public boolean getFilterTouchesWhenObscured() {
+        return (mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0;
+    }
+
+    /**
+     * Sets whether the framework should discard touches when the view's
+     * window is obscured by another visible window.
+     * Refer to the {@link View} security documentation for more details.
+     *
+     * @param enabled True if touch filtering should be enabled.
+     *
+     * @see #getFilterTouchesWhenObscured
+     * @attr ref android.R.styleable#View_filterTouchesWhenObscured
+     */
+    public void setFilterTouchesWhenObscured(boolean enabled) {
+        setFlags(enabled ? 0 : FILTER_TOUCHES_WHEN_OBSCURED,
+                FILTER_TOUCHES_WHEN_OBSCURED);
+    }
 
     /**
      * Indicates whether the entire hierarchy under this view will save its
@@ -3966,6 +4055,10 @@
      * @return True if the event was handled by the view, false otherwise.
      */
     public boolean dispatchTouchEvent(MotionEvent event) {
+        if (!onFilterTouchEventForSecurity(event)) {
+            return false;
+        }
+
         if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
                 mOnTouchListener.onTouch(this, event)) {
             return true;
@@ -3974,6 +4067,23 @@
     }
 
     /**
+     * Filter the touch event to apply security policies.
+     *
+     * @param event The motion event to be filtered.
+     * @return True if the event should be dispatched, false if the event should be dropped.
+     * 
+     * @see #getFilterTouchesWhenObscured
+     */
+    public boolean onFilterTouchEventForSecurity(MotionEvent event) {
+        if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
+                && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
+            // Window is obscured, drop this touch.
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Pass a trackball motion event down to the focused view.
      *
      * @param event The motion event to be dispatched.
@@ -4806,6 +4916,28 @@
     }
 
     /**
+     * Interface definition for a callback to be invoked when the layout bounds of a view
+     * changes due to layout processing.
+     */
+    public interface OnLayoutChangeListener {
+        /**
+         * Called when the focus state of a view has changed.
+         *
+         * @param v The view whose state has changed.
+         * @param left The new value of the view's left property.
+         * @param top The new value of the view's top property.
+         * @param right The new value of the view's right property.
+         * @param bottom The new value of the view's bottom property.
+         * @param oldLeft The previous value of the view's left property.
+         * @param oldTop The previous value of the view's top property.
+         * @param oldRight The previous value of the view's right property.
+         * @param oldBottom The previous value of the view's bottom property.
+         */
+        void onLayoutChange(View v, int left, int top, int right, int bottom,
+            int oldLeft, int oldTop, int oldRight, int oldBottom);
+    }
+
+    /**
      * This is called during layout when the size of this view has changed. If
      * you were just added to the view hierarchy, you're called with the old
      * values of 0.
@@ -5251,6 +5383,7 @@
      */
     public void setAlpha(float alpha) {
         mAlpha = alpha;
+        onSetAlpha((int) (alpha * 255));
         invalidate();
     }
 
@@ -5265,6 +5398,45 @@
     }
 
     /**
+     * Sets the top position of this view relative to its parent. This method is meant to be called
+     * by the layout system and should not generally be called otherwise, because the property
+     * may be changed at any time by the layout.
+     *
+     * @param top The top of this view, in pixels.
+     */
+    public final void setTop(int top) {
+        if (top != mTop) {
+            if (hasIdentityMatrix()) {
+                final ViewParent p = mParent;
+                if (p != null && mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    int minTop;
+                    int yLoc;
+                    if (top < mTop) {
+                        minTop = top;
+                        yLoc = top - mTop;
+                    } else {
+                        minTop = mTop;
+                        yLoc = 0;
+                    }
+                    r.set(0, yLoc, mRight - mLeft, mBottom - minTop);
+                    p.invalidateChild(this, r);
+                }
+            } else {
+                // Double-invalidation is necessary to capture view's old and new areas
+                invalidate();
+            }
+
+            mTop = top;
+
+            if (!mMatrixIsIdentity) {
+                mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+                invalidate();
+            }
+        }
+    }
+
+    /**
      * Bottom position of this view relative to its parent.
      *
      * @return The bottom of this view, in pixels.
@@ -5275,6 +5447,42 @@
     }
 
     /**
+     * Sets the bottom position of this view relative to its parent. This method is meant to be
+     * called by the layout system and should not generally be called otherwise, because the
+     * property may be changed at any time by the layout.
+     *
+     * @param bottom The bottom of this view, in pixels.
+     */
+    public final void setBottom(int bottom) {
+        if (bottom != mBottom) {
+            if (hasIdentityMatrix()) {
+                final ViewParent p = mParent;
+                if (p != null && mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    int maxBottom;
+                    if (bottom < mBottom) {
+                        maxBottom = mBottom;
+                    } else {
+                        maxBottom = bottom;
+                    }
+                    r.set(0, 0, mRight - mLeft, maxBottom - mTop);
+                    p.invalidateChild(this, r);
+                }
+            } else {
+                // Double-invalidation is necessary to capture view's old and new areas
+                invalidate();
+            }
+
+            mBottom = bottom;
+
+            if (!mMatrixIsIdentity) {
+                mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+                invalidate();
+            }
+        }
+    }
+
+    /**
      * Left position of this view relative to its parent.
      *
      * @return The left edge of this view, in pixels.
@@ -5285,6 +5493,46 @@
     }
 
     /**
+     * Sets the left position of this view relative to its parent. This method is meant to be called
+     * by the layout system and should not generally be called otherwise, because the property
+     * may be changed at any time by the layout.
+     *
+     * @param left The bottom of this view, in pixels.
+     */
+    public final void setLeft(int left) {
+        if (left != mLeft) {
+            System.out.println("view " + this + " left = " + left);
+            if (hasIdentityMatrix()) {
+                final ViewParent p = mParent;
+                if (p != null && mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    int minLeft;
+                    int xLoc;
+                    if (left < mLeft) {
+                        minLeft = left;
+                        xLoc = left - mLeft;
+                    } else {
+                        minLeft = mLeft;
+                        xLoc = 0;
+                    }
+                    r.set(xLoc, 0, mRight - minLeft, mBottom - mTop);
+                    p.invalidateChild(this, r);
+                }
+            } else {
+                // Double-invalidation is necessary to capture view's old and new areas
+                invalidate();
+            }
+
+            mLeft = left;
+
+            if (!mMatrixIsIdentity) {
+                mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+                invalidate();
+            }
+        }
+    }
+
+    /**
      * Right position of this view relative to its parent.
      *
      * @return The right edge of this view, in pixels.
@@ -5295,6 +5543,42 @@
     }
 
     /**
+     * Sets the right position of this view relative to its parent. This method is meant to be called
+     * by the layout system and should not generally be called otherwise, because the property
+     * may be changed at any time by the layout.
+     *
+     * @param right The bottom of this view, in pixels.
+     */
+    public final void setRight(int right) {
+        if (right != mRight) {
+            if (hasIdentityMatrix()) {
+                final ViewParent p = mParent;
+                if (p != null && mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    int maxRight;
+                    if (right < mRight) {
+                        maxRight = mRight;
+                    } else {
+                        maxRight = right;
+                    }
+                    r.set(0, 0, maxRight - mLeft, mBottom - mTop);
+                    p.invalidateChild(this, r);
+                }
+            } else {
+                // Double-invalidation is necessary to capture view's old and new areas
+                invalidate();
+            }
+
+            mRight = right;
+
+            if (!mMatrixIsIdentity) {
+                mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+                invalidate();
+            }
+        }
+    }
+
+    /**
      * The visual x position of this view, in pixels. This is equivalent to the
      * {@link #setTranslationX(float) translationX} property plus the current
      * {@link #getLeft() left} property. 
@@ -5536,15 +5820,12 @@
                     final Rect r = mAttachInfo.mTmpInvalRect;
                     int minLeft;
                     int maxRight;
-                    int xLoc;
                     if (offset < 0) {
                         minLeft = mLeft + offset;
                         maxRight = mRight;
-                        xLoc = offset;
                     } else {
                         minLeft = mLeft;
                         maxRight = mRight + offset;
-                        xLoc = 0;
                     }
                     r.set(0, 0, maxRight - minLeft, mBottom - mTop);
                     p.invalidateChild(this, r);
@@ -7103,9 +7384,6 @@
             if (ViewDebug.TRACE_HIERARCHY) {
                 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
             }
-            if (Config.DEBUG && ViewDebug.profileDrawing) {
-                EventLog.writeEvent(60002, hashCode());
-            }
 
             int width = mRight - mLeft;
             int height = mBottom - mTop;
@@ -7543,7 +7821,8 @@
         saveCount = canvas.getSaveCount();
 
         int solidColor = getSolidColor();
-        if (solidColor == 0) {
+        // TODO: Temporarily disable fading edges with hardware acceleration
+        if (solidColor == 0 && !canvas.isHardwareAccelerated()) {
             final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
 
             if (drawTop) {
@@ -7744,14 +8023,19 @@
      *
      * Derived classes with children should override
      * onLayout. In that method, they should
-     * call layout on each of their their children.
+     * call layout on each of their children.
      *
      * @param l Left position, relative to parent
      * @param t Top position, relative to parent
      * @param r Right position, relative to parent
      * @param b Bottom position, relative to parent
      */
+    @SuppressWarnings({"unchecked"})
     public final void layout(int l, int t, int r, int b) {
+        int oldL = mLeft;
+        int oldT = mTop;
+        int oldB = mBottom;
+        int oldR = mRight;
         boolean changed = setFrame(l, t, r, b);
         if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
             if (ViewDebug.TRACE_HIERARCHY) {
@@ -7760,6 +8044,15 @@
 
             onLayout(changed, l, t, r, b);
             mPrivateFlags &= ~LAYOUT_REQUIRED;
+
+            if (mOnLayoutChangeListeners != null) {
+                ArrayList<OnLayoutChangeListener> listenersCopy =
+                        (ArrayList<OnLayoutChangeListener>) mOnLayoutChangeListeners.clone();
+                int numListeners = listenersCopy.size();
+                for (int i = 0; i < numListeners; ++i) {
+                    listenersCopy.get(i).onLayoutChange(this, l, r, t, b, oldL, oldT, oldR, oldB);
+                }
+            }
         }
         mPrivateFlags &= ~FORCE_LAYOUT;
     }
@@ -7770,7 +8063,7 @@
      *
      * Derived classes with children should override
      * this method and call layout on each of
-     * their their children.
+     * their children.
      * @param changed This is a new size or position for this view
      * @param left Left position, relative to parent
      * @param top Top position, relative to parent
@@ -9921,11 +10214,6 @@
         final RectF mTmpTransformRect = new RectF();
 
         /**
-         * Temporary for use in computing invalidation areas with transformed views
-         */
-        final float[] mTmpTransformBounds = new float[8];
-
-        /**
          * Temporary list for use in collecting focusable descendents of a view.
          */
         final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 7b6991f..b1d5272 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -27,6 +27,7 @@
 import android.os.Environment;
 import android.os.Debug;
 import android.os.RemoteException;
+import dalvik.system.VMDebug;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -118,24 +119,21 @@
      *
      * @hide
      */
-    @Debug.DebugProperty
-    public static boolean profileDrawing = false;
+    public static final boolean DEBUG_PROFILE_DRAWING = false;
 
     /**
      * Profiles layout times in the events log.
      *
      * @hide
      */
-    @Debug.DebugProperty
-    public static boolean profileLayout = false;
+    public static final boolean DEBUG_PROFILE_LAYOUT = false;
 
     /**
      * Profiles real fps (times between draws) and displays the result.
      *
      * @hide
      */
-    @Debug.DebugProperty
-    public static boolean showFps = false;
+    public static final boolean DEBUG_SHOW_FPS = false;
 
     /**
      * <p>Enables or disables views consistency check. Even when this property is enabled,
@@ -427,7 +425,7 @@
      * @hide
      */
     public static long getViewInstanceCount() {
-        return View.sInstanceCount;
+        return VMDebug.countInstancesOfClass(View.class);
     }
 
     /**
@@ -438,7 +436,7 @@
      * @hide
      */
     public static long getViewRootInstanceCount() {
-        return ViewRoot.getInstanceCount();
+        return VMDebug.countInstancesOfClass(ViewRoot.class);
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9770313..c8b26ef 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.animation.LayoutTransition;
 import com.android.internal.R;
 
 import android.content.Context;
@@ -31,11 +32,8 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.util.AttributeSet;
-import android.util.Config;
-import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.ViewGroup.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -43,7 +41,6 @@
 import android.view.animation.Transformation;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 
 /**
  * <p>
@@ -66,8 +63,10 @@
  * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
  * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
  * @attr ref android.R.styleable#ViewGroup_descendantFocusability
+ * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
  */
 public abstract class ViewGroup extends View implements ViewParent, ViewManager {
+
     private static final boolean DBG = false;
 
     /**
@@ -300,6 +299,14 @@
     // Used to draw cached views
     private final Paint mCachePaint = new Paint();
 
+    // Used to animate add/remove changes in layout
+    private LayoutTransition mTransition;
+
+    // The set of views that are currently being transitioned. This list is used to track views
+    // being removed that should not actually be removed from the parent yet because they are
+    // being animated.
+    private ArrayList<View> mTransitioningViews;
+
     public ViewGroup(Context context) {
         super(context);
         initViewGroup();
@@ -374,6 +381,12 @@
                 case R.styleable.ViewGroup_splitMotionEvents:
                     setMotionEventSplittingEnabled(a.getBoolean(attr, false));
                     break;
+                case R.styleable.ViewGroup_animateLayoutChanges:
+                    boolean animateLayoutChanges = a.getBoolean(attr, false);
+                    if (animateLayoutChanges) {
+                        setLayoutTransition(new LayoutTransition());
+                    }
+                    break;
             }
         }
 
@@ -855,12 +868,16 @@
      */
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (!onFilterTouchEventForSecurity(ev)) {
+            return false;
+        }
+
         if ((mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS) {
             if (mSplitMotionTargets == null) {
                 mSplitMotionTargets = new SplitMotionTargets();
             }
             return dispatchSplitTouchEvent(ev);
-        }
+	}
 
         final int action = ev.getAction();
         final float xf = ev.getX();
@@ -1970,6 +1987,7 @@
             }
         } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
             child.onSetAlpha(255);
+            child.mPrivateFlags &= ~ALPHA_SET;
         }
 
         if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
@@ -2004,9 +2022,6 @@
                 cachePaint.setAlpha(255);
                 mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
             }
-            if (Config.DEBUG && ViewDebug.profileDrawing) {
-                EventLog.writeEvent(60003, hashCode());
-            }
             canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
         }
 
@@ -2328,6 +2343,10 @@
                     "You must call removeView() on the child's parent first.");
         }
 
+        if (mTransition != null) {
+            mTransition.childAdd(this, child);
+        }
+
         if (!checkLayoutParams(params)) {
             params = generateLayoutParams(params);
         }
@@ -2405,7 +2424,9 @@
     // This method also sets the child's mParent to null
     private void removeFromArray(int index) {
         final View[] children = mChildren;
-        children[index].mParent = null;
+        if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
+            children[index].mParent = null;
+        }
         final int count = mChildrenCount;
         if (index == count - 1) {
             children[--mChildrenCount] = null;
@@ -2540,13 +2561,19 @@
     }
 
     private void removeViewInternal(int index, View view) {
+
+        if (mTransition != null) {
+            mTransition.childRemove(this, view);
+        }
+
         boolean clearChildFocus = false;
         if (view == mFocused) {
             view.clearFocusForRemoval();
             clearChildFocus = true;
         }
 
-        if (view.getAnimation() != null) {
+        if (view.getAnimation() != null ||
+                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
             addDisappearingView(view);
         } else if (view.mAttachInfo != null) {
            view.dispatchDetachedFromWindow();
@@ -2565,6 +2592,36 @@
         }
     }
 
+    /**
+     * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
+     * not null, changes in layout which occur because of children being added to or removed from
+     * the ViewGroup will be animated according to the animations defined in that LayoutTransition
+     * object. By default, the transition object is null (so layout changes are not animated).
+     *
+     * @param transition The LayoutTransition object that will animated changes in layout. A value
+     * of <code>null</code> means no transition will run on layout changes.
+     * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
+     */
+    public void setLayoutTransition(LayoutTransition transition) {
+        mTransition = transition;
+        if (mTransition != null) {
+            mTransition.addTransitionListener(mLayoutTransitionListener);
+        }
+    }
+
+    /**
+     * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
+     * not null, changes in layout which occur because of children being added to or removed from
+     * the ViewGroup will be animated according to the animations defined in that LayoutTransition
+     * object. By default, the transition object is null (so layout changes are not animated).
+     *
+     * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
+     * A value of <code>null</code> means no transition will run on layout changes.
+     */
+    public LayoutTransition getLayoutTransition() {
+        return mTransition;
+    }
+
     private void removeViewsInternal(int start, int count) {
         final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
         final boolean notifyListener = onHierarchyChangeListener != null;
@@ -2578,12 +2635,17 @@
         for (int i = start; i < end; i++) {
             final View view = children[i];
 
+            if (mTransition != null) {
+                mTransition.childRemove(this, view);
+            }
+
             if (view == focused) {
                 view.clearFocusForRemoval();
                 clearChildFocus = view;
             }
 
-            if (view.getAnimation() != null) {
+            if (view.getAnimation() != null ||
+                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
                 addDisappearingView(view);
             } else if (detach) {
                view.dispatchDetachedFromWindow();
@@ -2642,12 +2704,17 @@
         for (int i = count - 1; i >= 0; i--) {
             final View view = children[i];
 
+            if (mTransition != null) {
+                mTransition.childRemove(this, view);
+            }
+
             if (view == focused) {
                 view.clearFocusForRemoval();
                 clearChildFocus = view;
             }
 
-            if (view.getAnimation() != null) {
+            if (view.getAnimation() != null ||
+                    (mTransitioningViews != null && mTransitioningViews.contains(view))) {
                 addDisappearingView(view);
             } else if (detach) {
                view.dispatchDetachedFromWindow();
@@ -2680,11 +2747,16 @@
      * @see #detachViewFromParent(int)
      */
     protected void removeDetachedView(View child, boolean animate) {
+        if (mTransition != null) {
+            mTransition.childRemove(this, child);
+        }
+
         if (child == mFocused) {
             child.clearFocus();
         }
 
-        if (animate && child.getAnimation() != null) {
+        if ((animate && child.getAnimation() != null) ||
+                (mTransitioningViews != null && mTransitioningViews.contains(child))) {
             addDisappearingView(child);
         } else if (child.mAttachInfo != null) {
             child.dispatchDetachedFromWindow();
@@ -3659,6 +3731,41 @@
         }
     }
 
+    private LayoutTransition.TransitionListener mLayoutTransitionListener =
+            new LayoutTransition.TransitionListener() {
+        @Override
+        public void startTransition(LayoutTransition transition, ViewGroup container,
+                View view, int transitionType) {
+            // We only care about disappearing items, since we need special logic to keep
+            // those items visible after they've been 'removed'
+            if (transitionType == LayoutTransition.DISAPPEARING) {
+                if (mTransitioningViews == null) {
+                    mTransitioningViews = new ArrayList<View>();
+                }
+                mTransitioningViews.add(view);
+            }
+        }
+
+        @Override
+        public void endTransition(LayoutTransition transition, ViewGroup container,
+                View view, int transitionType) {
+            if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
+                mTransitioningViews.remove(view);
+                final ArrayList<View> disappearingChildren = mDisappearingChildren;
+                if (disappearingChildren != null && disappearingChildren.contains(view)) {
+                    disappearingChildren.remove(view);
+                    if (view.mAttachInfo != null) {
+                        view.dispatchDetachedFromWindow();
+                    }
+                    if (view.mParent != null) {
+                        view.mParent = null;
+                    }
+                    mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
+                }
+            }
+        }
+    };
+
     /**
      * {@inheritDoc}
      */
@@ -4190,18 +4297,10 @@
             return mTargets.get(id);
         }
 
-        public int indexOfId(int id) {
-            return mTargets.indexOfKey(id);
-        }
-
         public int indexOfTarget(View target) {
             return mTargets.indexOfValue(target);
         }
 
-        public int idAt(int index) {
-            return mTargets.keyAt(index);
-        }
-
         public View targetAt(int index) {
             return mTargets.valueAt(index);
         }
@@ -4224,17 +4323,6 @@
             return null;
         }
 
-        public boolean hasTarget(View target) {
-            final TargetInfo[] unique = mUniqueTargets;
-            final int uniqueCount = mUniqueTargetCount;
-            for (int i = 0; i < uniqueCount; i++) {
-                if (unique[i].view == target) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
         public boolean isEmpty() {
             return mUniqueTargetCount == 0;
         }
@@ -4384,11 +4472,10 @@
                         (newActionIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
             }
 
-            MotionEvent result = MotionEvent.obtain(downTime, ev.getEventTime(),
+            return MotionEvent.obtain(downTime, ev.getEventTime(),
                     action, pointerCount, mPointerIds, mPointerCoords, ev.getMetaState(),
                     ev.getXPrecision(), ev.getYPrecision(), ev.getDeviceId(), ev.getEdgeFlags(),
-                    ev.getSource());
-            return result;
+                    ev.getSource(), ev.getFlags());
         }
 
         static class TargetInfo {
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 7f1c0fb..e5fc859 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -89,8 +89,6 @@
      */
     static final int MAX_TRACKBALL_DELAY = 250;
 
-    static long sInstanceCount = 0;
-
     static IWindowSession sWindowSession;
 
     static final Object mStaticInit = new Object();
@@ -234,9 +232,6 @@
             }
         }
 
-        // For debug only
-        //++sInstanceCount;
-
         // Initialize the statics when this class is first instantiated. This is
         // done here instead of in the static block because Zygote does not
         // allow the spawning of threads.
@@ -263,19 +258,6 @@
         mDensity = context.getResources().getDisplayMetrics().densityDpi;
     }
 
-    // For debug only
-    /*
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        --sInstanceCount;
-    }
-    */
-
-    public static long getInstanceCount() {
-        return sInstanceCount;
-    }
-
     public static void addFirstDrawHandler(Runnable callback) {
         synchronized (sFirstDrawHandlers) {
             if (!sFirstDrawComplete) {
@@ -464,7 +446,7 @@
         // Only enable hardware acceleration if we are not in the system process
         // The window manager creates ViewRoots to display animated preview windows
         // of launching apps and we don't want those to be hardware accelerated
-        if (Process.myUid() != Process.SYSTEM_UID) {
+        if (!HardwareRenderer.sRendererDisabled) {
             // Try to enable hardware acceleration if requested
             if (attrs != null &&
                     (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
@@ -964,8 +946,8 @@
                     mSurfaceHolder.mSurfaceLock.unlock();
                 }
             }
-            
-            if (hwIntialized) {
+
+            if (hwIntialized || (windowShouldResize && mHwRenderer != null)) {
                 mHwRenderer.setup(mWidth, mHeight);
             }
 
@@ -1026,7 +1008,7 @@
                 TAG, "Laying out " + host + " to (" +
                 host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")");
             long startTime = 0L;
-            if (ViewDebug.profileLayout) {
+            if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
                 startTime = SystemClock.elapsedRealtime();
             }
             host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
@@ -1039,7 +1021,7 @@
                 }
             }
 
-            if (ViewDebug.profileLayout) {
+            if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
                 EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
             }
 
@@ -1339,7 +1321,7 @@
                         //canvas.drawARGB(255, 255, 0, 0);
                     }
 
-                    if (ViewDebug.profileDrawing) {
+                    if (ViewDebug.DEBUG_PROFILE_DRAWING) {
                         startTime = SystemClock.elapsedRealtime();
                     }
 
@@ -1382,7 +1364,7 @@
                         mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
                     }
 
-                    if (SHOW_FPS || ViewDebug.showFps) {
+                    if (SHOW_FPS || ViewDebug.DEBUG_SHOW_FPS) {
                         int now = (int)SystemClock.elapsedRealtime();
                         if (sDrawTime != 0) {
                             nativeShowFPS(canvas, now - sDrawTime);
@@ -1390,7 +1372,7 @@
                         sDrawTime = now;
                     }
 
-                    if (ViewDebug.profileDrawing) {
+                    if (ViewDebug.DEBUG_PROFILE_DRAWING) {
                         EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
                     }
                 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 659f9cd..76701a9 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -74,6 +74,8 @@
     public final static int FLAG_MENU = 0x00000040;
     public final static int FLAG_LAUNCHER = 0x00000080;
 
+    public final static int FLAG_INJECTED = 0x01000000;
+
     public final static int FLAG_WOKE_HERE = 0x10000000;
     public final static int FLAG_BRIGHT_HERE = 0x20000000;
 
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index c090f8e..32ff647 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -35,7 +35,7 @@
 public class AnimationUtils {
 
     /**
-     * These flags are used when parsing Sequencer objects
+     * These flags are used when parsing AnimatorSet objects
      */
     private static final int TOGETHER = 0;
     private static final int SEQUENTIALLY = 1;
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index 7c5f2b0..c1319e5 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -47,7 +47,7 @@
         String retVal = inUrl;
         WebAddress webAddress;
 
-        Log.v(LOGTAG, "guessURL before queueRequest: " + inUrl);
+        if (DebugFlags.URL_UTIL) Log.v(LOGTAG, "guessURL before queueRequest: " + inUrl);
 
         if (inUrl.length() == 0) return inUrl;
         if (inUrl.startsWith("about:")) return inUrl;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 7519b0d..d81d7f2 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -211,6 +211,7 @@
     private boolean         mNavDump = false;
     private boolean         mSupportZoom = true;
     private boolean         mBuiltInZoomControls = false;
+    private boolean         mDisplayZoomControls = true;
     private boolean         mAllowFileAccess = true;
     private boolean         mLoadWithOverviewMode = false;
     private boolean         mEnableSmoothTransition = false;
@@ -508,6 +509,26 @@
     }
 
     /**
+     * Sets whether the on screen zoom buttons are used.
+     * A combination of built in zoom controls enabled
+     * and on screen zoom controls disabled allows for pinch to zoom
+     * to work without the on screen controls
+     * @hide
+     */
+    public void setDisplayZoomControls(boolean enabled) {
+        mDisplayZoomControls = enabled;
+        mWebView.updateMultiTouchSupport(mContext);
+    }
+
+    /**
+     * Returns true if the on screen zoom buttons are being used.
+     * @hide
+     */
+    public boolean getDisplayZoomControls() {
+        return mDisplayZoomControls;
+    }
+
+    /**
      * Enable or disable file access within WebView. File access is enabled by
      * default.
      */
@@ -1108,6 +1129,7 @@
      * @deprecated This method is no longer used as plugins are loaded from
      * their own APK via the system's package manager.
      */
+    @Deprecated
     public synchronized void setPluginsPath(String pluginsPath) {
     }
 
@@ -1296,6 +1318,7 @@
      * @deprecated This method is no longer used as plugins are loaded from
      * their own APK via the system's package manager.
      */
+    @Deprecated
     public synchronized String getPluginsPath() {
         return "";
     }
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 990f891..6c11a7f 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -110,6 +110,11 @@
     // FIXME: This can be replaced with TextView.NO_FILTERS if that
     // is made public/protected.
     private static final InputFilter[] NO_FILTERS = new InputFilter[0];
+    // For keeping track of the fact that the delete key was pressed, so
+    // we can simply pass a delete key instead of calling deleteSelection.
+    private boolean mGotDelete;
+    private int mDelSelStart;
+    private int mDelSelEnd;
 
     /**
      * Create a new WebTextView.
@@ -159,9 +164,16 @@
         // However, if the cursor is at the beginning of the field, which
         // includes the case where it has zero length, then the text is not
         // changed, so send the events immediately.
-        if (KeyEvent.KEYCODE_DEL == keyCode && oldStart == 0 && oldEnd == 0) {
-            sendDomEvent(event);
-            return true;
+        if (KeyEvent.KEYCODE_DEL == keyCode) {
+            if (oldStart == 0 && oldEnd == 0) {
+                sendDomEvent(event);
+                return true;
+            }
+            if (down) {
+                mGotDelete = true;
+                mDelSelStart = oldStart;
+                mDelSelEnd = oldEnd;
+            }
         }
 
         if ((mSingle && KeyEvent.KEYCODE_ENTER == keyCode)) {
@@ -412,17 +424,38 @@
         mPreChange = postChange;
         if (0 == count) {
             if (before > 0) {
+                // For this and all changes to the text, update our cache
+                updateCachedTextfield();
+                if (mGotDelete) {
+                    mGotDelete = false;
+                    int oldEnd = start + before;
+                    if (mDelSelEnd == oldEnd
+                            && (mDelSelStart == start
+                            || (mDelSelStart == oldEnd && before == 1))) {
+                        // If the selection is set up properly before the
+                        // delete, send the DOM events.
+                        sendDomEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
+                                KeyEvent.KEYCODE_DEL));
+                        sendDomEvent(new KeyEvent(KeyEvent.ACTION_UP,
+                                KeyEvent.KEYCODE_DEL));
+                        return;
+                    }
+                }
                 // This was simply a delete or a cut, so just delete the
                 // selection.
                 mWebView.deleteSelection(start, start + before);
-                // For this and all changes to the text, update our cache
-                updateCachedTextfield();
             }
+            mGotDelete = false;
             // before should never be negative, so whether it was a cut
             // (handled above), or before is 0, in which case nothing has
             // changed, we should return.
             return;
         }
+        // Ensure that this flag gets cleared, since with autocorrect on, a
+        // delete key press may have a more complex result than deleting one
+        // character or the existing selection, so it will not get cleared
+        // above.
+        mGotDelete = false;
         // Find the last character being replaced.  If it can be represented by
         // events, we will pass them to native (after replacing the beginning
         // of the changed text), so we can see javascript events.
@@ -594,8 +627,10 @@
      */
     /* package */ void remove() {
         // hide the soft keyboard when the edit text is out of focus
-        InputMethodManager.getInstance(mContext).hideSoftInputFromWindow(
-                getWindowToken(), 0);
+        InputMethodManager imm = InputMethodManager.getInstance(mContext);
+        if (imm.isActive(this)) {
+            imm.hideSoftInputFromWindow(getWindowToken(), 0);
+        }
         mWebView.removeView(this);
         mWebView.requestFocus();
     }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 0b236af..b83edc7 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3701,8 +3701,10 @@
     private void hideSoftKeyboard() {
         InputMethodManager imm = (InputMethodManager)
                 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-
-        imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
+        if (imm.isActive(this)
+                || (inEditingMode() && imm.isActive(mWebTextView))) {
+            imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
+        }
     }
 
     /*
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 33ebcf5..1323217 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -856,7 +856,8 @@
     private ZoomControlBase getCurrentZoomControl() {
         if (mWebView.getSettings() != null && mWebView.getSettings().supportZoom()) {
             if (mWebView.getSettings().getBuiltInZoomControls()) {
-                if (mEmbeddedZoomControl == null) {
+                if ((mEmbeddedZoomControl == null)
+                        && mWebView.getSettings().getDisplayZoomControls()) {
                     mEmbeddedZoomControl = new ZoomControlEmbedded(this, mWebView);
                 }
                 return mEmbeddedZoomControl;
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 0636d72..e8d96c5 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -18,12 +18,10 @@
 
 import java.util.ArrayList;
 
-import android.animation.PropertyAnimator;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.graphics.RectF;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcel;
@@ -31,6 +29,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 
@@ -128,6 +127,13 @@
     boolean mShouldLoop = true;
 
     /**
+     * The width and height of some child, used as a size reference in-case our
+     * dimensions are unspecified by the parent.
+     */
+    int mReferenceChildWidth = -1;
+    int mReferenceChildHeight = -1;
+
+    /**
      * TODO: Animation stuff is still in flux, waiting on the new framework to settle a bit.
      */
     Animation mInAnimation;
@@ -212,13 +218,13 @@
      * @param view The view that is being animated
      */
     void animateViewForTransition(int fromIndex, int toIndex, View view) {
-        PropertyAnimator pa;
+        ObjectAnimator pa;
         if (fromIndex == -1) {
             view.setAlpha(0.0f);
-            pa = new PropertyAnimator(400, view, "alpha", 0.0f, 1.0f);
+            pa = new ObjectAnimator(400, view, "alpha", 0.0f, 1.0f);
             pa.start();
         } else if (toIndex == -1) {
-            pa = new PropertyAnimator(400, view, "alpha", 1.0f, 0.0f);
+            pa = new ObjectAnimator(400, view, "alpha", 1.0f, 0.0f);
             pa.start();
         }
     }
@@ -414,7 +420,7 @@
                         FrameLayout fl = new FrameLayout(mContext);
                         fl.addView(newView);
                         mActiveViews[index] = fl;
-                        addViewInLayout(fl, -1, createOrReuseLayoutParams(fl));
+                        addChild(fl);
                         applyTransformForChildAtIndex(fl, newRelativeIndex);
                         animateViewForTransition(-1, newRelativeIndex, fl);
                     }
@@ -451,6 +457,64 @@
         }
     }
 
+    private void addChild(View child) {
+        addViewInLayout(child, -1, createOrReuseLayoutParams(child));
+
+        // This code is used to obtain a reference width and height of a child in case we need
+        // to decide our own size. TODO: Do we want to update the size of the child that we're
+        // using for reference size? If so, when?
+        if (mReferenceChildWidth == -1 || mReferenceChildHeight == -1) {
+            int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+            child.measure(measureSpec, measureSpec);
+            mReferenceChildWidth = child.getMeasuredWidth();
+            mReferenceChildHeight = child.getMeasuredHeight();
+        }
+    }
+
+    private void measureChildren() {
+        final int count = getChildCount();
+        final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight;
+        final int childHeight = mMeasuredHeight - mPaddingTop - mPaddingBottom;
+
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+        final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+
+        boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1);
+
+        // We need to deal with the case where our parent hasn't told us how
+        // big we should be. In this case we try to use the desired size of the first
+        // child added.
+        if (heightSpecMode == MeasureSpec.UNSPECIFIED) {
+            heightSpecSize = haveChildRefSize ? mReferenceChildHeight + mPaddingTop +
+                    mPaddingBottom : 0;
+        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+            heightSpecSize = haveChildRefSize ? Math.min(mReferenceChildHeight + mPaddingTop +
+                    mPaddingBottom, heightSpecSize) : 0;
+        }
+
+        if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
+            widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
+                    mPaddingRight : 0;
+        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+            widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft +
+                    mPaddingRight, widthSpecSize) : 0;
+        }
+
+        setMeasuredDimension(widthSpecSize, heightSpecSize);
+        measureChildren();
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         boolean dataChanged = mDataChanged;
@@ -472,8 +536,7 @@
             int childRight = mPaddingLeft + child.getMeasuredWidth();
             int childBottom = mPaddingTop + child.getMeasuredHeight();
 
-            child.layout(mPaddingLeft, mPaddingTop,
-                    childRight, childBottom);
+            child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
         }
         mDataChanged = false;
     }
@@ -538,31 +601,6 @@
         setDisplayedChild(mWhichChild);
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int count = getChildCount();
-
-        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
-        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
-
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-            lp.width = widthSpecSize - mPaddingLeft - mPaddingRight;
-            lp.height = heightSpecSize - mPaddingTop - mPaddingBottom;
-
-            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
-                    MeasureSpec.EXACTLY);
-            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
-                    MeasureSpec.EXACTLY);
-
-            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
-        }
-        setMeasuredDimension(widthSpecSize, heightSpecSize);
-    }
-
     /**
      * Shows only the specified child. The other displays Views exit the screen
      * with the {@link #getOutAnimation() out animation} and the specified child
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 12ff292..b464fef 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -706,7 +706,8 @@
             if (mItemClickListener != null) {
                 final DropDownListView list = mDropDownList;
                 final View child = list.getChildAt(position - list.getFirstVisiblePosition());
-                mItemClickListener.onItemClick(list, child, position, child.getId());
+                final ListAdapter adapter = list.getAdapter();
+                mItemClickListener.onItemClick(list, child, position, adapter.getItemId(position));
             }
             return true;
         }
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index 50fbb6b..5598c65 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -247,6 +247,7 @@
                         trigger = true;
                         createUri = Uri.fromParts("tel", (String)cookie, null);
 
+                        //$FALL-THROUGH$
                     case TOKEN_PHONE_LOOKUP: {
                         if (cursor != null && cursor.moveToFirst()) {
                             long contactId = cursor.getLong(PHONE_ID_COLUMN_INDEX);
@@ -260,12 +261,14 @@
                         trigger = true;
                         createUri = Uri.fromParts("mailto", (String)cookie, null);
 
+                        //$FALL-THROUGH$
                     case TOKEN_EMAIL_LOOKUP: {
                         if (cursor != null && cursor.moveToFirst()) {
                             long contactId = cursor.getLong(EMAIL_ID_COLUMN_INDEX);
                             String lookupKey = cursor.getString(EMAIL_LOOKUP_STRING_COLUMN_INDEX);
                             lookupUri = Contacts.getLookupUri(contactId, lookupKey);
                         }
+                        break;
                     }
 
                     case TOKEN_CONTACT_LOOKUP_AND_TRIGGER: {
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 58c2613..c0cbb19 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -53,25 +53,93 @@
      */
     public static final int MODE_DROPDOWN = 1;
     
+    /**
+     * Use the theme-supplied value to select the dropdown mode.
+     */
+    private static final int MODE_THEME = -1;
+    
     private SpinnerPopup mPopup;
     private DropDownAdapter mTempAdapter;
-    
+
+    /**
+     * Construct a new spinner with the given context's theme.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     */
     public Spinner(Context context) {
         this(context, null);
     }
 
+    /**
+     * Construct a new spinner with the given context's theme and the supplied
+     * mode of displaying choices. <code>mode</code> may be one of
+     * {@link #MODE_DIALOG} or {@link #MODE_DROPDOWN}.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     * @param mode Constant describing how the user will select choices from the spinner.
+     * 
+     * @see #MODE_DIALOG
+     * @see #MODE_DROPDOWN
+     */
+    public Spinner(Context context, int mode) {
+        this(context, null, com.android.internal.R.attr.spinnerStyle, mode);
+    }
+
+    /**
+     * Construct a new spinner with the given context's theme and the supplied attribute set.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     */
     public Spinner(Context context, AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.spinnerStyle);
     }
 
+    /**
+     * Construct a new spinner with the given context's theme, the supplied attribute set,
+     * and default style.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyle The default style to apply to this view. If 0, no style
+     *        will be applied (beyond what is included in the theme). This may
+     *        either be an attribute resource, whose value will be retrieved
+     *        from the current theme, or an explicit style resource.
+     */
     public Spinner(Context context, AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, MODE_THEME);
+    }
+
+    /**
+     * Construct a new spinner with the given context's theme, the supplied attribute set,
+     * and default style. <code>mode</code> may be one of {@link #MODE_DIALOG} or
+     * {@link #MODE_DROPDOWN} and determines how the user will select choices from the spinner.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyle The default style to apply to this view. If 0, no style
+     *        will be applied (beyond what is included in the theme). This may
+     *        either be an attribute resource, whose value will be retrieved
+     *        from the current theme, or an explicit style resource.
+     * @param mode Constant describing how the user will select choices from the spinner.
+     * 
+     * @see #MODE_DIALOG
+     * @see #MODE_DROPDOWN
+     */
+    public Spinner(Context context, AttributeSet attrs, int defStyle, int mode) {
         super(context, attrs, defStyle);
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.Spinner, defStyle, 0);
-        
-        final int mode = a.getInt(com.android.internal.R.styleable.Spinner_spinnerMode,
-                MODE_DIALOG);
+
+        if (mode == MODE_THEME) {
+            mode = a.getInt(com.android.internal.R.styleable.Spinner_spinnerMode, MODE_DIALOG);
+        }
         
         switch (mode) {
         case MODE_DIALOG: {
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 9816b39..0a97904 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -16,11 +16,11 @@
 
 package android.widget;
 
-import java.util.WeakHashMap;
-
-import android.animation.PropertyAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -28,6 +28,7 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.TableMaskFilter;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -35,6 +36,8 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup.LayoutParams;
 import android.view.animation.LinearInterpolator;
 import android.widget.RemoteViews.RemoteView;
 
@@ -53,6 +56,19 @@
     private final int MINIMUM_ANIMATION_DURATION = 50;
 
     /**
+     * Parameters effecting the perspective visuals
+     */
+    private static float PERSPECTIVE_SHIFT_FACTOR = 0.12f;
+    private static float PERSPECTIVE_SCALE_FACTOR = 0.35f;
+
+    /**
+     * Represent the two possible stack modes, one where items slide up, and the other
+     * where items slide down. The perspective is also inverted between these two modes.
+     */
+    private static final int ITEMS_SLIDE_UP = 0;
+    private static final int ITEMS_SLIDE_DOWN = 1;
+
+    /**
      * These specify the different gesture states
      */
     private static final int GESTURE_NONE = 0;
@@ -66,8 +82,6 @@
     private static final float SWIPE_THRESHOLD_RATIO = 0.35f;
     private static final float SLIDE_UP_RATIO = 0.7f;
 
-    private final WeakHashMap<View, Float> mRotations = new WeakHashMap<View, Float>();
-
     /**
      * Sentinel value for no current active pointer.
      * Used by {@link #mActivePointerId}.
@@ -75,6 +89,12 @@
     private static final int INVALID_POINTER = -1;
 
     /**
+     * Number of active views in the stack. One fewer view is actually visible, as one is hidden.
+     */
+    private static final int NUM_ACTIVE_VIEWS = 5;
+
+
+    /**
      * These variables are all related to the current state of touch interaction
      * with the stack
      */
@@ -95,6 +115,7 @@
     private boolean mFirstLayoutHappened = false;
     private ViewGroup mAncestorContainingAllChildren = null;
     private int mAncestorHeight = 0;
+    private int mStackMode;
 
     public StackView(Context context) {
         super(context);
@@ -107,7 +128,7 @@
     }
 
     private void initStackView() {
-        configureViewAnimator(4, 2, false);
+        configureViewAnimator(NUM_ACTIVE_VIEWS, NUM_ACTIVE_VIEWS - 2, false);
         setStaticTransformationsEnabled(true);
         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
         mTouchSlop = configuration.getScaledTouchSlop();
@@ -125,6 +146,11 @@
         setClipChildren(false);
         setClipToPadding(false);
 
+        // This sets the form of the StackView, which is currently to have the perspective-shifted
+        // views above the active view, and have items slide down when sliding out. The opposite is
+        // available by using ITEMS_SLIDE_UP.
+        mStackMode = ITEMS_SLIDE_DOWN;
+
         // This is a flag to indicate the the stack is loading for the first time
         mWhichChild = -1;
     }
@@ -140,7 +166,7 @@
             }
             view.setVisibility(VISIBLE);
 
-            PropertyAnimator fadeIn = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
+            ObjectAnimator<Float> fadeIn = new ObjectAnimator<Float>(DEFAULT_ANIMATION_DURATION,
                     view, "alpha", view.getAlpha(), 1.0f);
             fadeIn.start();
         } else if (fromIndex == mNumActiveViews - 1 && toIndex == mNumActiveViews - 2) {
@@ -148,49 +174,32 @@
             view.setVisibility(VISIBLE);
 
             LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            int largestDuration =
-                Math.round(mStackSlider.getDurationForNeutralPosition()*DEFAULT_ANIMATION_DURATION);
-
-            int duration = largestDuration;
-            if (mYVelocity != 0) {
-                duration = 1000*(0 - lp.verticalOffset)/Math.abs(mYVelocity);
-            }
-
-            duration = Math.min(duration, largestDuration);
-            duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
+            int duration = Math.round(mStackSlider.getDurationForNeutralPosition(mYVelocity));
 
             StackSlider animationSlider = new StackSlider(mStackSlider);
-            PropertyAnimator slideInY = new PropertyAnimator(duration, animationSlider,
-                    "YProgress", mStackSlider.getYProgress(), 0);
-            slideInY.setInterpolator(new LinearInterpolator());
-            slideInY.start();
-            PropertyAnimator slideInX = new PropertyAnimator(duration, animationSlider,
-                    "XProgress", mStackSlider.getXProgress(), 0);
-            slideInX.setInterpolator(new LinearInterpolator());
-            slideInX.start();
+            PropertyValuesHolder<Float> slideInY =
+                    new PropertyValuesHolder<Float>("YProgress", 0.0f);
+            PropertyValuesHolder<Float> slideInX =
+                    new PropertyValuesHolder<Float>("XProgress", 0.0f);
+            ObjectAnimator pa = new ObjectAnimator(duration, animationSlider,
+                    slideInX, slideInY);
+            pa.setInterpolator(new LinearInterpolator());
+            pa.start();
         } else if (fromIndex == mNumActiveViews - 2 && toIndex == mNumActiveViews - 1) {
             // Slide item out
             LayoutParams lp = (LayoutParams) view.getLayoutParams();
 
-            int largestDuration = Math.round(mStackSlider.getDurationForOffscreenPosition()*
-                    DEFAULT_ANIMATION_DURATION);
-            int duration = largestDuration;
-            if (mYVelocity != 0) {
-                duration = 1000*(lp.verticalOffset + mViewHeight)/Math.abs(mYVelocity);
-            }
-
-            duration = Math.min(duration, largestDuration);
-            duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
+            int duration = Math.round(mStackSlider.getDurationForOffscreenPosition(mYVelocity));
 
             StackSlider animationSlider = new StackSlider(mStackSlider);
-            PropertyAnimator slideOutY = new PropertyAnimator(duration, animationSlider,
-                    "YProgress", mStackSlider.getYProgress(), 1);
-            slideOutY.setInterpolator(new LinearInterpolator());
-            slideOutY.start();
-            PropertyAnimator slideOutX = new PropertyAnimator(duration, animationSlider,
-                    "XProgress", mStackSlider.getXProgress(), 0);
-            slideOutX.setInterpolator(new LinearInterpolator());
-            slideOutX.start();
+            PropertyValuesHolder<Float> slideOutY =
+                    new PropertyValuesHolder<Float>("YProgress", 1.0f);
+            PropertyValuesHolder<Float> slideOutX =
+                    new PropertyValuesHolder<Float>("XProgress", 0.0f);
+            ObjectAnimator pa = new ObjectAnimator(duration, animationSlider,
+                   slideOutX, slideOutY);
+            pa.setInterpolator(new LinearInterpolator());
+            pa.start();
         } else if (fromIndex == -1 && toIndex == mNumActiveViews - 1) {
             // Make sure this view that is "waiting in the wings" is invisible
             view.setAlpha(0.0f);
@@ -199,28 +208,41 @@
             lp.setVerticalOffset(-mViewHeight);
         } else if (toIndex == -1) {
             // Fade item out
-            PropertyAnimator fadeOut = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
-                    view, "alpha", view.getAlpha(), 0);
+            ObjectAnimator<Float> fadeOut = new ObjectAnimator<Float>
+                    (DEFAULT_ANIMATION_DURATION, view, "alpha", view.getAlpha(), 0.0f);
             fadeOut.start();
         }
+
+        // Implement the faked perspective
+        if (toIndex != -1) {
+            float maxPerpectiveShift = mViewHeight * PERSPECTIVE_SHIFT_FACTOR;
+            int index = toIndex;
+
+            if (toIndex == mNumActiveViews -1) index--;
+
+            float r = (index * 1.0f) / (mNumActiveViews - 2);
+
+            float scale = 1 - PERSPECTIVE_SCALE_FACTOR * (1 - r);
+            PropertyValuesHolder<Float> scaleX = new PropertyValuesHolder<Float>("scaleX", scale);
+            PropertyValuesHolder<Float> scaleY = new PropertyValuesHolder<Float>("scaleY", scale);
+
+            r = (float) Math.pow(r, 2);
+
+            int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1;
+            float transY = -stackDirection * r * maxPerpectiveShift +
+                    stackDirection * (1 - scale) * (mViewHeight / 2.0f);
+
+            PropertyValuesHolder<Float> translationY =
+                    new PropertyValuesHolder<Float>("translationY", transY);
+            ObjectAnimator pa = new ObjectAnimator(100, view, scaleX, scaleY, translationY);
+            pa.start();
+        }
     }
 
     /**
      * Apply any necessary tranforms for the child that is being added.
      */
     void applyTransformForChildAtIndex(View child, int relativeIndex) {
-        if (!mRotations.containsKey(child)) {
-            float rotation = (float) (Math.random()*26 - 13);
-            mRotations.put(child, rotation);
-            child.setRotation(rotation);
-        }
-
-        // Child has been removed
-        if (relativeIndex == -1) {
-            if (mRotations.containsKey(child)) {
-                mRotations.remove(child);
-            }
-        }
     }
 
     @Override
@@ -248,8 +270,8 @@
 
     private void onLayout() {
         if (!mFirstLayoutHappened) {
-            mViewHeight = Math.round(SLIDE_UP_RATIO*getMeasuredHeight());
-            mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight);
+            mViewHeight = Math.round(SLIDE_UP_RATIO * getMeasuredHeight());
+            mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO * mViewHeight);
             mFirstLayoutHappened = true;
         }
     }
@@ -299,8 +321,14 @@
             cancelLongPress();
             requestDisallowInterceptTouchEvent(true);
 
-            int activeIndex = swipeGestureType == GESTURE_SLIDE_DOWN ? mNumActiveViews - 1
-                    : mNumActiveViews - 2;
+            int activeIndex;
+            if (mStackMode == ITEMS_SLIDE_UP) {
+                activeIndex = (swipeGestureType == GESTURE_SLIDE_DOWN) ?
+                        mNumActiveViews - 1 : mNumActiveViews - 2;
+            } else {
+                activeIndex = (swipeGestureType == GESTURE_SLIDE_DOWN) ?
+                        mNumActiveViews - 2 : mNumActiveViews - 1;
+            }
 
             if (mAdapter == null) return;
 
@@ -317,6 +345,8 @@
             if (v == null) return;
 
             mHighlight.setImageBitmap(sHolographicHelper.createOutline(v));
+            mHighlight.setRotation(v.getRotation());
+            mHighlight.setTranslationY(v.getTranslationY());
             mHighlight.bringToFront();
             v.bringToFront();
             mStackSlider.setView(v);
@@ -352,14 +382,16 @@
             case MotionEvent.ACTION_MOVE: {
                 beginGestureIfNeeded(deltaY);
 
-                float rx = deltaX/(mViewHeight*1.0f);
+                float rx = deltaX / (mViewHeight * 1.0f);
                 if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
-                    float r = (deltaY-mTouchSlop*1.0f)/mViewHeight*1.0f;
+                    float r = (deltaY - mTouchSlop * 1.0f) / mViewHeight * 1.0f;
+                    if (mStackMode == ITEMS_SLIDE_DOWN) r = 1 - r;
                     mStackSlider.setYProgress(1 - r);
                     mStackSlider.setXProgress(rx);
                     return true;
                 } else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
-                    float r = -(deltaY + mTouchSlop*1.0f)/mViewHeight*1.0f;
+                    float r = -(deltaY + mTouchSlop * 1.0f) / mViewHeight * 1.0f;
+                    if (mStackMode == ITEMS_SLIDE_DOWN) r = 1 - r;
                     mStackSlider.setYProgress(r);
                     mStackSlider.setXProgress(rx);
                     return true;
@@ -447,41 +479,59 @@
         if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN
                 && mStackSlider.mMode == StackSlider.NORMAL_MODE) {
             // Swipe threshold exceeded, swipe down
-            showNext();
+            if (mStackMode == ITEMS_SLIDE_UP) {
+                showNext();
+            } else {
+                showPrevious();
+            }
             mHighlight.bringToFront();
         } else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP
                 && mStackSlider.mMode == StackSlider.NORMAL_MODE) {
             // Swipe threshold exceeded, swipe up
-            showPrevious();
+            if (mStackMode == ITEMS_SLIDE_UP) {
+                showPrevious();
+            } else {
+                showNext();
+            }
+
             mHighlight.bringToFront();
-        } else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
+        } else if (mSwipeGestureType == GESTURE_SLIDE_UP ) {
             // Didn't swipe up far enough, snap back down
-            int duration =
-                Math.round(mStackSlider.getDurationForNeutralPosition()*DEFAULT_ANIMATION_DURATION);
+            int duration;
+            float finalYProgress = (mStackMode == ITEMS_SLIDE_DOWN) ? 1 : 0;
+            if (mStackMode == ITEMS_SLIDE_UP || mStackSlider.mMode != StackSlider.NORMAL_MODE) {
+                duration = Math.round(mStackSlider.getDurationForNeutralPosition());
+            } else {
+                duration = Math.round(mStackSlider.getDurationForOffscreenPosition());
+            }
 
             StackSlider animationSlider = new StackSlider(mStackSlider);
-            PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
-                    "YProgress", mStackSlider.getYProgress(), 0);
-            snapBackY.setInterpolator(new LinearInterpolator());
-            snapBackY.start();
-            PropertyAnimator snapBackX = new PropertyAnimator(duration, animationSlider,
-                    "XProgress", mStackSlider.getXProgress(), 0);
-            snapBackX.setInterpolator(new LinearInterpolator());
-            snapBackX.start();
+            PropertyValuesHolder<Float> snapBackY =
+                    new PropertyValuesHolder<Float>("YProgress", finalYProgress);
+            PropertyValuesHolder<Float> snapBackX =
+                    new PropertyValuesHolder<Float>("XProgress", 0.0f);
+            ObjectAnimator pa = new ObjectAnimator(duration, animationSlider,
+                    snapBackX, snapBackY);
+            pa.setInterpolator(new LinearInterpolator());
+            pa.start();
         } else if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
             // Didn't swipe down far enough, snap back up
-            int duration = Math.round(mStackSlider.getDurationForOffscreenPosition()*
-                    DEFAULT_ANIMATION_DURATION);
+            float finalYProgress = (mStackMode == ITEMS_SLIDE_DOWN) ? 0 : 1;
+            int duration;
+            if (mStackMode == ITEMS_SLIDE_DOWN || mStackSlider.mMode != StackSlider.NORMAL_MODE) {
+                duration = Math.round(mStackSlider.getDurationForNeutralPosition());
+            } else {
+                duration = Math.round(mStackSlider.getDurationForOffscreenPosition());
+            }
 
             StackSlider animationSlider = new StackSlider(mStackSlider);
-            PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
-                    "YProgress", mStackSlider.getYProgress(), 1);
-            snapBackY.setInterpolator(new LinearInterpolator());
-            snapBackY.start();
-            PropertyAnimator snapBackX = new PropertyAnimator(duration, animationSlider,
-                    "XProgress", mStackSlider.getXProgress(), 0);
-            snapBackX.setInterpolator(new LinearInterpolator());
-            snapBackX.start();
+            PropertyValuesHolder<Float> snapBackY =
+                    new PropertyValuesHolder<Float>("YProgress", finalYProgress);
+            PropertyValuesHolder<Float> snapBackX =
+                    new PropertyValuesHolder<Float>("XProgress", 0.0f);
+            ObjectAnimator pa = new ObjectAnimator(duration, animationSlider,
+                    snapBackX, snapBackY);
+            pa.start();
         }
 
         mActivePointerId = INVALID_POINTER;
@@ -510,22 +560,22 @@
         }
 
         private float cubic(float r) {
-            return (float) (Math.pow(2*r-1, 3) + 1)/2.0f;
+            return (float) (Math.pow(2 * r - 1, 3) + 1) / 2.0f;
         }
 
         private float highlightAlphaInterpolator(float r) {
             float pivot = 0.4f;
             if (r < pivot) {
-                return 0.85f*cubic(r/pivot);
+                return 0.85f * cubic(r / pivot);
             } else {
-                return 0.85f*cubic(1 - (r-pivot)/(1-pivot));
+                return 0.85f * cubic(1 - (r - pivot) / (1 - pivot));
             }
         }
 
         private float viewAlphaInterpolator(float r) {
             float pivot = 0.3f;
             if (r > pivot) {
-                return (r - pivot)/(1 - pivot);
+                return (r - pivot) / (1 - pivot);
             } else {
                 return 0;
             }
@@ -536,7 +586,7 @@
             if (r < pivot) {
                 return 0;
             } else {
-                return (r-pivot)/(1-pivot);
+                return (r - pivot) / (1 - pivot);
             }
         }
 
@@ -553,13 +603,15 @@
             final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
             final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
 
+            int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1;
+
             switch (mMode) {
                 case NORMAL_MODE:
-                    viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
-                    highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
+                    viewLp.setVerticalOffset(Math.round(-r * stackDirection * mViewHeight));
+                    highlightLp.setVerticalOffset(Math.round(-r * stackDirection * mViewHeight));
                     mHighlight.setAlpha(highlightAlphaInterpolator(r));
 
-                    float alpha = viewAlphaInterpolator(1-r);
+                    float alpha = viewAlphaInterpolator(1 - r);
 
                     // We make sure that views which can't be seen (have 0 alpha) are also invisible
                     // so that they don't interfere with click events.
@@ -571,19 +623,19 @@
                     }
 
                     mView.setAlpha(alpha);
-                    mView.setRotationX(90.0f*rotationInterpolator(r));
-                    mHighlight.setRotationX(90.0f*rotationInterpolator(r));
+                    mView.setRotationX(stackDirection * 90.0f * rotationInterpolator(r));
+                    mHighlight.setRotationX(stackDirection * 90.0f * rotationInterpolator(r));
                     break;
                 case BEGINNING_OF_STACK_MODE:
-                    r = r*0.2f;
-                    viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
-                    highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
+                    r = r * 0.2f;
+                    viewLp.setVerticalOffset(Math.round(-stackDirection * r * mViewHeight));
+                    highlightLp.setVerticalOffset(Math.round(-stackDirection * r * mViewHeight));
                     mHighlight.setAlpha(highlightAlphaInterpolator(r));
                     break;
                 case END_OF_STACK_MODE:
-                    r = (1-r)*0.2f;
-                    viewLp.setVerticalOffset(Math.round(r*mViewHeight));
-                    highlightLp.setVerticalOffset(Math.round(r*mViewHeight));
+                    r = (1-r) * 0.2f;
+                    viewLp.setVerticalOffset(Math.round(stackDirection * r * mViewHeight));
+                    highlightLp.setVerticalOffset(Math.round(stackDirection * r * mViewHeight));
                     mHighlight.setAlpha(highlightAlphaInterpolator(r));
                     break;
             }
@@ -600,8 +652,8 @@
             final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
 
             r *= 0.2f;
-            viewLp.setHorizontalOffset(Math.round(r*mViewHeight));
-            highlightLp.setHorizontalOffset(Math.round(r*mViewHeight));
+            viewLp.setHorizontalOffset(Math.round(r * mViewHeight));
+            highlightLp.setHorizontalOffset(Math.round(r * mViewHeight));
         }
 
         void setMode(int mode) {
@@ -609,31 +661,51 @@
         }
 
         float getDurationForNeutralPosition() {
-            return getDuration(false);
+            return getDuration(false, 0);
         }
 
         float getDurationForOffscreenPosition() {
-            return getDuration(mMode == END_OF_STACK_MODE ? false : true);
+            return getDuration(true, 0);
         }
 
-        private float getDuration(boolean invert) {
+        float getDurationForNeutralPosition(float velocity) {
+            return getDuration(false, velocity);
+        }
+
+        float getDurationForOffscreenPosition(float velocity) {
+            return getDuration(true, velocity);
+        }
+
+        private float getDuration(boolean invert, float velocity) {
             if (mView != null) {
                 final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
 
-                float d = (float) Math.sqrt(Math.pow(viewLp.horizontalOffset,2) +
-                        Math.pow(viewLp.verticalOffset,2));
+                float d = (float) Math.sqrt(Math.pow(viewLp.horizontalOffset, 2) +
+                        Math.pow(viewLp.verticalOffset, 2));
                 float maxd = (float) Math.sqrt(Math.pow(mViewHeight, 2) +
-                        Math.pow(0.4f*mViewHeight, 2));
-                return invert ? (1-d/maxd) : d/maxd;
+                        Math.pow(0.4f * mViewHeight, 2));
+
+                if (velocity == 0) {
+                    return (invert ? (1 - d / maxd) : d / maxd) * DEFAULT_ANIMATION_DURATION;
+                } else {
+                    float duration = invert ? d / Math.abs(velocity) :
+                            (maxd - d) / Math.abs(velocity);
+                    if (duration < MINIMUM_ANIMATION_DURATION ||
+                            duration > DEFAULT_ANIMATION_DURATION) {
+                        return getDuration(invert, 0);
+                    } else {
+                        return duration;
+                    }
+                }
             }
             return 0;
         }
 
-        float getYProgress() {
+        public float getYProgress() {
             return mYProgress;
         }
 
-        float getXProgress() {
+        public float getXProgress() {
             return mXProgress;
         }
     }
@@ -654,6 +726,8 @@
             LayoutParams lp = (LayoutParams) currentLp;
             lp.setHorizontalOffset(0);
             lp.setVerticalOffset(0);
+            lp.width = 0;
+            lp.width = 0;
             return lp;
         }
         return new LayoutParams(v);
@@ -684,15 +758,59 @@
             child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
                     childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
 
-            //TODO: temp until fix in View
-            child.setPivotX(child.getMeasuredWidth()/2);
-            child.setPivotY(child.getMeasuredHeight()/2);
         }
 
         mDataChanged = false;
         onLayout();
     }
 
+    private void measureChildren() {
+        final int count = getChildCount();
+        final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight;
+        final int childHeight = Math.round(mMeasuredHeight*(1-PERSPECTIVE_SHIFT_FACTOR))
+                - mPaddingTop - mPaddingBottom;
+
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+        final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+
+        boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1);
+
+        // We need to deal with the case where our parent hasn't told us how
+        // big we should be. In this case we should
+        float factor = 1/(1 - PERSPECTIVE_SHIFT_FACTOR);
+        if (heightSpecMode == MeasureSpec.UNSPECIFIED) {
+            heightSpecSize = haveChildRefSize ?
+                    Math.round(mReferenceChildHeight * (1 + factor)) +
+                    mPaddingTop + mPaddingBottom : 0;
+        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+            heightSpecSize = haveChildRefSize ? Math.min(
+                    Math.round(mReferenceChildHeight * (1 + factor)) + mPaddingTop +
+                    mPaddingBottom, heightSpecSize) : 0;
+        }
+
+        if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
+            widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
+                    mPaddingRight : 0;
+        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+            widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft +
+                    mPaddingRight, widthSpecSize) : 0;
+        }
+
+        setMeasuredDimension(widthSpecSize, heightSpecSize);
+        measureChildren();
+    }
+
     class LayoutParams extends ViewGroup.LayoutParams {
         int horizontalOffset;
         int verticalOffset;
@@ -700,6 +818,8 @@
 
         LayoutParams(View view) {
             super(0, 0);
+            width = 0;
+            height = 0;
             horizontalOffset = 0;
             verticalOffset = 0;
             mView = view;
@@ -709,6 +829,8 @@
             super(c, attrs);
             horizontalOffset = 0;
             verticalOffset = 0;
+            width = 0;
+            height = 0;
         }
 
         private Rect parentRect = new Rect();
@@ -731,6 +853,10 @@
                 gp = (View) p.getParent();
                 parentRect.set(p.getLeft() - gp.getScrollX(), p.getTop() - gp.getScrollY(),
                         p.getRight() - gp.getScrollX(), p.getBottom() - gp.getScrollY());
+
+                // TODO: we need to stop early here if we've hit the edge of the screen
+                // so as to prevent us from walking too high in the hierarchy. A lot of this
+                // code might become a lot more straightforward.
             }
 
             if (depth > mAncestorHeight) {
@@ -744,7 +870,7 @@
 
         private Rect invalidateRect = new Rect();
         private RectF invalidateRectf = new RectF();
-        // This is public so that PropertyAnimator can access it
+        // This is public so that ObjectAnimator can access it
         public void setVerticalOffset(int newVerticalOffset) {
             int offsetDelta = newVerticalOffset - verticalOffset;
             verticalOffset = newVerticalOffset;
@@ -799,7 +925,7 @@
     private static class HolographicHelper {
         private final Paint mHolographicPaint = new Paint();
         private final Paint mErasePaint = new Paint();
-        private final float STROKE_WIDTH = 3.0f;
+        private final Paint mBlurPaint = new Paint();
 
         HolographicHelper() {
             initializePaints();
@@ -808,8 +934,10 @@
         void initializePaints() {
             mHolographicPaint.setColor(0xff6699ff);
             mHolographicPaint.setFilterBitmap(true);
+            mHolographicPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
             mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
             mErasePaint.setFilterBitmap(true);
+            mBlurPaint.setMaskFilter(new BlurMaskFilter(2, BlurMaskFilter.Blur.NORMAL));
         }
 
         Bitmap createOutline(View v) {
@@ -822,31 +950,31 @@
             Canvas canvas = new Canvas(bitmap);
 
             float rotationX = v.getRotationX();
+            float rotation = v.getRotation();
+            float translationY = v.getTranslationY();
             v.setRotationX(0);
+            v.setRotation(0);
+            v.setTranslationY(0);
             canvas.concat(v.getMatrix());
             v.draw(canvas);
-
             v.setRotationX(rotationX);
+            v.setRotation(rotation);
+            v.setTranslationY(translationY);
+            canvas.setMatrix(id);
 
             drawOutline(canvas, bitmap);
             return bitmap;
         }
 
         final Matrix id = new Matrix();
-        final Matrix scaleMatrix = new Matrix();
         void drawOutline(Canvas dest, Bitmap src) {
-            Bitmap mask = src.extractAlpha();
-
+            int[] xy = new int[2];
+            Bitmap mask = src.extractAlpha(mBlurPaint, xy);
+            Canvas maskCanvas = new Canvas(mask);
+            maskCanvas.drawBitmap(src, -xy[0], -xy[1], mErasePaint);
             dest.drawColor(0, PorterDuff.Mode.CLEAR);
-
-            float xScale = STROKE_WIDTH*2/(dest.getWidth());
-            float yScale = STROKE_WIDTH*2/(dest.getHeight());
-
-            scaleMatrix.reset();
-            scaleMatrix.preScale(1+xScale, 1+yScale, dest.getWidth()/2, dest.getHeight()/2);
             dest.setMatrix(id);
-            dest.drawBitmap(mask, scaleMatrix, mHolographicPaint);
-            dest.drawBitmap(mask, id, mErasePaint);
+            dest.drawBitmap(mask, xy[0], xy[1], mHolographicPaint);
             mask.recycle();
         }
     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9961438..dabe7ba 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -21,8 +21,8 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.content.ClipboardManager;
 import android.content.ClipData;
+import android.content.ClipboardManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
@@ -36,6 +36,7 @@
 import android.graphics.RectF;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
+import android.inputmethodservice.ExtractEditText;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -3704,18 +3705,21 @@
 
         boolean changed = false;
 
+        SelectionModifierCursorController selectionController = null;
+        if (mSelectionModifierCursorController != null) {
+            selectionController = (SelectionModifierCursorController)
+                mSelectionModifierCursorController;
+        }
+
+
         if (mMovement != null) {
             /* This code also provides auto-scrolling when a cursor is moved using a
              * CursorController (insertion point or selection limits).
              * For selection, ensure start or end is visible depending on controller's state.
              */
             int curs = getSelectionEnd();
-            if (mSelectionModifierCursorController != null) {
-                SelectionModifierCursorController selectionController =
-                    (SelectionModifierCursorController) mSelectionModifierCursorController;
-                if (selectionController.isSelectionStartDragged()) {
-                    curs = getSelectionStart();
-                }
+            if (selectionController != null && selectionController.isSelectionStartDragged()) {
+                curs = getSelectionStart();
             }
 
             /*
@@ -3735,10 +3739,16 @@
             changed = bringTextIntoView();
         }
 
-        if (mShouldStartSelectionActionMode) {
+        // This has to be checked here since:
+        // - onFocusChanged cannot start it when focus is given to a view with selected text (after
+        //   a screen rotation) since layout is not yet initialized at that point.
+        // - ExtractEditText does not call onFocus when it is displayed. Fixing this issue would
+        //   allow to test for hasSelection in onFocusChanged, which would trigger a
+        //   startTextSelectionMode here. TODO
+        if (selectionController != null && hasSelection()) {
             startSelectionActionMode();
-            mShouldStartSelectionActionMode = false;
         }
+
         mPreDrawState = PREDRAW_DONE;
         return !changed;
     }
@@ -4350,6 +4360,15 @@
                 if (shouldAdvanceFocusOnEnter()) {
                     return 0;
                 }
+                break;
+
+                // Has to be done on key down (and not on key up) to correctly be intercepted.
+            case KeyEvent.KEYCODE_BACK:
+                if (mSelectionActionMode != null) {
+                    stopSelectionActionMode();
+                    return -1;
+                }
+                break;
         }
 
         if (mInput != null) {
@@ -4503,6 +4522,7 @@
 
                     return super.onKeyUp(keyCode, event);
                 }
+                break;
         }
 
         if (mInput != null)
@@ -6434,11 +6454,6 @@
                 sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
                 mBeforeText = null;
             }
-
-            // TODO. The cursor controller should hide as soon as text is typed.
-            // But this method is also used for cosmetic changes (underline current word when
-            // spell corrections are displayed. There is currently no way to make the difference
-            // between these cosmetic changes and actual text modifications.
         }
 
         public void afterTextChanged(Editable buffer) {
@@ -6530,13 +6545,9 @@
             int selEnd = getSelectionEnd();
 
             if (!mFrozenWithFocus || (selStart < 0 || selEnd < 0)) {
-                boolean selMoved = mSelectionMoved;
-
-                if (mSelectionModifierCursorController != null) {
-                    final int touchOffset = 
-                        ((SelectionModifierCursorController) mSelectionModifierCursorController).
-                        getMinTouchOffset();
-                    Selection.setSelection((Spannable) mText, touchOffset);
+                // Has to be done before onTakeFocus, which can be overloaded.
+                if (mLastTouchOffset >= 0) {
+                    Selection.setSelection((Spannable) mText, mLastTouchOffset);
                 }
 
                 if (mMovement != null) {
@@ -6547,7 +6558,12 @@
                     Selection.setSelection((Spannable) mText, 0, mText.length());
                 }
 
-                if (selMoved && selStart >= 0 && selEnd >= 0) {
+                // The DecorView does not have focus when the 'Done' ExtractEditText button is
+                // pressed. Since it is the ViewRoot's mView, it requests focus before
+                // ExtractEditText clears focus, which gives focus to the ExtractEditText.
+                // This special case ensure that we keep current selection in that case.
+                // It would be better to know why the DecorView does not have focus at that time.
+                if (((this instanceof ExtractEditText) || mSelectionMoved) && selStart >= 0 && selEnd >= 0) {
                     /*
                      * Someone intentionally set the selection, so let them
                      * do whatever it is that they wanted to do instead of
@@ -6557,7 +6573,6 @@
                      * just setting the selection in theirs and we still
                      * need to go through that path.
                      */
-
                     Selection.setSelection((Spannable) mText, selStart, selEnd);
                 }
                 mTouchFocusSelected = true;
@@ -6576,13 +6591,6 @@
             if (mError != null) {
                 showError();
             }
-
-            // We cannot start the selection mode immediately. The layout may be null here and is
-            // needed by the cursor controller. Layout creation is deferred up to drawing. The
-            // selection action mode will be started in onPreDraw().
-            if (selStart != selEnd) {
-                mShouldStartSelectionActionMode = true;
-            }
         } else {
             if (mError != null) {
                 hideError();
@@ -6591,14 +6599,22 @@
             onEndBatchEdit();
 
             hideInsertionPointCursorController();
-            terminateSelectionActionMode();
+            if (this instanceof ExtractEditText) {
+                // terminateTextSelectionMode removes selection, which we want to keep when
+                // ExtractEditText goes out of focus.
+                final int selStart = getSelectionStart();
+                final int selEnd = getSelectionEnd();
+                terminateSelectionActionMode();
+                Selection.setSelection((Spannable) mText, selStart, selEnd);
+            } else {
+                terminateSelectionActionMode();
+            }
         }
 
         startStopMarquee(focused);
 
         if (mTransformation != null) {
-            mTransformation.onFocusChanged(this, mText, focused, direction,
-                                           previouslyFocusedRect);
+            mTransformation.onFocusChanged(this, mText, focused, direction, previouslyFocusedRect);
         }
 
         super.onFocusChanged(focused, direction, previouslyFocusedRect);
@@ -6657,59 +6673,57 @@
         }
     }
 
-    class CommitSelectionReceiver extends ResultReceiver {
-        private final int mPrevStart, mPrevEnd;
-        private final int mNewStart, mNewEnd;
+    private void onTapUpEvent(int prevStart, int prevEnd) {
+        final int start = getSelectionStart();
+        final int end = getSelectionEnd();
 
-        public CommitSelectionReceiver(int mPrevStart, int mPrevEnd, int mNewStart, int mNewEnd) {
-            super(getHandler());
-            this.mPrevStart = mPrevStart;
-            this.mPrevEnd = mPrevEnd;
-            this.mNewStart = mNewStart;
-            this.mNewEnd = mNewEnd;
-        }
-
-        @Override
-        protected void onReceiveResult(int resultCode, Bundle resultData) {
-            int start = mNewStart;
-            int end = mNewEnd;
-
-            // Move the cursor to the new position, unless this tap was actually
-            // use to show the IMM. Leave cursor unchanged in that case.
-            if (resultCode == InputMethodManager.RESULT_SHOWN) {
-                start = mPrevStart;
-                end = mPrevEnd;
+        if (start == end) {
+            if (start >= prevStart && start < prevEnd) {
+                // Tapping inside the selection displays the cut/copy/paste context menu.
+                showContextMenu();
+                return;
             } else {
-                if ((mPrevStart != mPrevEnd) && (start == end)) {
-                    if ((start >= mPrevStart) && (start <= mPrevEnd)) {
-                        // Tapping inside the selection does nothing
-                        Selection.setSelection((Spannable) mText, mPrevStart, mPrevEnd);
-                        return;
-                    } else {
-                        // Tapping outside stops selection mode, if any
-                        stopSelectionActionMode();
-                    }
-                }
+                // Tapping outside stops selection mode, if any
+                stopSelectionActionMode();
 
                 if (mInsertionPointCursorController != null) {
                     mInsertionPointCursorController.show();
                 }
             }
-
-            final int len = mText.length();
-            if (start > len) {
-                start = len;
-            }
-            if (end > len) {
-                end = len;
-            }
-            Selection.setSelection((Spannable)mText, start, end);
         }
     }
-    
+
+    class CommitSelectionReceiver extends ResultReceiver {
+        private final int mPrevStart, mPrevEnd;
+
+        public CommitSelectionReceiver(int prevStart, int prevEnd) {
+            super(getHandler());
+            mPrevStart = prevStart;
+            mPrevEnd = prevEnd;
+        }
+
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            // If this tap was actually used to show the IMM, leave cursor or selection unchanged
+            // by restoring its previous position.
+            if (resultCode == InputMethodManager.RESULT_SHOWN) {
+                final int len = mText.length();
+                int start = Math.min(len, mPrevStart);
+                int end = Math.min(len, mPrevEnd);
+                Selection.setSelection((Spannable)mText, start, end);
+
+                if (hasSelection()) {
+                    startSelectionActionMode();
+                } else if (mInsertionPointCursorController != null) {
+                    mInsertionPointCursorController.show();
+                }
+            }
+        }
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        final int action = event.getAction();
+        final int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN) {
             // Reset this state; it will be re-set if super.onTouchEvent
             // causes focus to move to the view.
@@ -6729,11 +6743,7 @@
             return superResult;
         }
 
-        if ((mMovement != null || onCheckIsTextEditor()) &&
-                mText instanceof Spannable && mLayout != null) {
-            
-            int oldSelStart = getSelectionStart();
-            int oldSelEnd = getSelectionEnd();
+        if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) {
 
             if (mInsertionPointCursorController != null) {
                 mInsertionPointCursorController.onTouchEvent(event);
@@ -6744,6 +6754,10 @@
 
             boolean handled = false;
 
+            // Save previous selection, in case this event is used to show the IME.
+            int oldSelStart = getSelectionStart();
+            int oldSelEnd = getSelectionEnd();
+
             if (mMovement != null) {
                 handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
             }
@@ -6753,17 +6767,17 @@
                     InputMethodManager imm = (InputMethodManager)
                           getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
 
-                    final int newSelStart = getSelectionStart();
-                    final int newSelEnd = getSelectionEnd();
-
                     CommitSelectionReceiver csr = null;
-                    if (newSelStart != oldSelStart || newSelEnd != oldSelEnd ||
+                    if (getSelectionStart() != oldSelStart || getSelectionEnd() != oldSelEnd ||
                             didTouchFocusSelect()) {
-                        csr = new CommitSelectionReceiver(oldSelStart, oldSelEnd,
-                                newSelStart, newSelEnd);
+                        csr = new CommitSelectionReceiver(oldSelStart, oldSelEnd);
                     }
 
                     handled |= imm.showSoftInput(this, 0, csr) && (csr != null);
+
+                    // Cannot be done by CommitSelectionReceiver, which might not always be called,
+                    // for instance when dealing with an ExtractEditText.
+                    onTapUpEvent(oldSelStart, oldSelEnd);
                 }
             }
 
@@ -7313,10 +7327,11 @@
     public boolean onTextContextMenuItem(int id) {
         int min = 0;
         int max = mText.length();
-        
+
         if (isFocused()) {
-            int selStart = getSelectionStart();
-            int selEnd = getSelectionEnd();
+            final int selStart = getSelectionStart();
+            final int selEnd = getSelectionEnd();
+
             min = Math.max(0, Math.min(selStart, selEnd));
             max = Math.max(0, Math.max(selStart, selEnd));
         }
@@ -7326,7 +7341,6 @@
 
         switch (id) {
             case ID_COPY_URL:
-                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
 
                 URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
                 if (urls.length >= 1) {
@@ -7649,7 +7663,7 @@
         Handle(Drawable drawable) {
             mDrawable = drawable;
         }
-        
+
         void positionAtCursor(final int offset, boolean bottom) {
             final int drawableWidth = mDrawable.getIntrinsicWidth();
             final int drawableHeight = mDrawable.getIntrinsicHeight();
@@ -7660,8 +7674,9 @@
             mHotSpotVerticalPosition = lineTop;
 
             final Rect bounds = sCursorControllerTempRect;
-            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0);
-            bounds.top = (bottom ? lineBottom : lineTop) - drawableHeight / 2;
+            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0)
+                + mScrollX;
+            bounds.top = (bottom ? lineBottom : lineTop) - drawableHeight / 2 + mScrollY;
 
             mTopExtension = bottom ? 0 : drawableHeight / 2;
             mBottomExtension = drawableHeight;
@@ -7669,7 +7684,7 @@
             // Extend touch region up when editing the last line of text (or a single line) so that
             // it is easier to grab.
             if (line == mLayout.getLineCount() - 1) {
-                  mTopExtension = (lineBottom - lineTop) - drawableHeight / 2;
+                mTopExtension = (lineBottom - lineTop) - drawableHeight / 2;
             }
 
             bounds.right = bounds.left + drawableWidth;
@@ -7692,6 +7707,7 @@
                            (int) (y - mBottomExtension),
                            (int) (x + drawableWidth / 2.0),
                            (int) (y + mTopExtension));
+            fingerRect.offset(mScrollX, mScrollY);
             return Rect.intersects(mDrawable.getBounds(), fingerRect);
         }
 
@@ -7985,7 +8001,7 @@
                         final int y = (int) event.getY();
 
                         // Remember finger down position, to be able to start selection from there
-                        mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
+                        mMinTouchOffset = mMaxTouchOffset = mLastTouchOffset = getOffset(x, y);
 
                         if (mIsVisible) {
                             if (mMovement instanceof ArrowKeyMovementMethod) {
@@ -8001,7 +8017,8 @@
                                     // In case both controllers are under finger (very small
                                     // selection region), arbitrarily pick end controller.
                                     mStartIsDragged = !isOnEnd;
-                                    final Handle draggedHandle = mStartIsDragged ? mStartHandle : mEndHandle;
+                                    final Handle draggedHandle =
+                                        mStartIsDragged ? mStartHandle : mEndHandle;
                                     final Rect bounds = draggedHandle.mDrawable.getBounds();
                                     mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
                                     mOffsetY = draggedHandle.mHotSpotVerticalPosition - y;
@@ -8166,8 +8183,8 @@
     // Cursor Controllers. Null when disabled.
     private CursorController        mInsertionPointCursorController;
     private CursorController        mSelectionModifierCursorController;
-    private boolean                 mShouldStartSelectionActionMode = false;
     private ActionMode              mSelectionActionMode;
+    private int                     mLastTouchOffset = -1;
     // Created once and shared by different CursorController helper methods.
     // Only one cursor controller is active at any time which prevent race conditions.
     private static Rect             sCursorControllerTempRect = new Rect();
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 1048352..cf029d2 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -122,17 +122,6 @@
     }
 
     @Override
-    public void setStandardNavigationMode(int titleResId, int subtitleResId) {
-        setStandardNavigationMode(mContext.getString(titleResId),
-                mContext.getString(subtitleResId));
-    }
-
-    @Override
-    public void setStandardNavigationMode(int titleResId) {
-        setStandardNavigationMode(mContext.getString(titleResId));
-    }
-
-    @Override
     public void setTitle(int resId) {
         setTitle(mContext.getString(resId));
     }
@@ -169,19 +158,6 @@
         mActionView.setCallback(null);
     }
 
-    public void setStandardNavigationMode(CharSequence title) {
-        cleanupTabs();
-        setStandardNavigationMode(title, null);
-    }
-
-    public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
-        cleanupTabs();
-        mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
-        mActionView.setTitle(title);
-        mActionView.setSubtitle(subtitle);
-        mActionView.setCallback(null);
-    }
-
     public void setSelectedNavigationItem(int position) {
         switch (mActionView.getNavigationMode()) {
         case NAVIGATION_MODE_TABS:
@@ -211,17 +187,7 @@
         if (mSelectedTab != null) {
             selectTab(null);
         }
-        if (!mTabs.isEmpty()) {
-            if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
-                final FragmentTransaction trans = mActivity.openFragmentTransaction();
-                final int tabCount = mTabs.size();
-                for (int i = 0; i < tabCount; i++) {
-                    trans.remove(mTabs.get(i).getFragment());
-                }
-                trans.commit();
-            }
-            mTabs.clear();
-        }
+        mTabs.clear();
     }
 
     public void setTitle(CharSequence title) {
@@ -295,31 +261,23 @@
     private void configureTab(Tab tab, int position) {
         final TabImpl tabi = (TabImpl) tab;
         final boolean isFirstTab = mTabs.isEmpty();
-        final FragmentTransaction trans = mActivity.openFragmentTransaction();
-        final Fragment frag = tabi.getFragment();
+        final ActionBar.TabListener callback = tabi.getCallback();
+
+        if (callback == null) {
+            throw new IllegalStateException("Action Bar Tab must have a Callback");
+        }
 
         tabi.setPosition(position);
         mTabs.add(position, tabi);
 
-        if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
-            if (!frag.isAdded()) {
-                trans.add(mTabContainerViewId, frag);
-            }
-        }
-
         if (isFirstTab) {
-            if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
-                trans.show(frag);
-            } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
-                trans.add(mTabContainerViewId, frag);
-            }
+            final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction();
             mSelectedTab = tabi;
-        } else {
-            if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
-                trans.hide(frag);
+            callback.onTabSelected(tab, trans);
+            if (!trans.isEmpty()) {
+                trans.commit();
             }
         }
-        trans.commit();
     }
 
     @Override
@@ -329,8 +287,8 @@
     }
 
     @Override
-    public void insertTab(Tab tab, int position) {
-        mActionView.insertTab(tab, position);
+    public void addTab(Tab tab, int position) {
+        mActionView.addTab(tab, position);
         configureTab(tab, position);
     }
 
@@ -367,35 +325,29 @@
     }
 
     @Override
-    public void setTabNavigationMode(int containerViewId) {
-        mTabContainerViewId = containerViewId;
-        setTabNavigationMode();
-    }
-
-    @Override
     public void selectTab(Tab tab) {
         if (mSelectedTab == tab) {
             return;
         }
 
         mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
-        final FragmentTransaction trans = mActivity.openFragmentTransaction();
+        final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction();
         if (mSelectedTab != null) {
-            if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
-                trans.hide(mSelectedTab.getFragment());
-            } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
-                trans.remove(mSelectedTab.getFragment());
-            }
-        }
-        if (tab != null) {
-            if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
-                trans.show(tab.getFragment());
-            } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
-                trans.add(mTabContainerViewId, tab.getFragment());
-            }
+            mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
         }
         mSelectedTab = (TabImpl) tab;
-        trans.commit();
+        if (mSelectedTab != null) {
+            mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans);
+        }
+
+        if (!trans.isEmpty()) {
+            trans.commit();
+        }
+    }
+
+    @Override
+    public Tab getSelectedTab() {
+        return mSelectedTab;
     }
 
     @Override
@@ -542,14 +494,40 @@
      * @hide
      */
     public class TabImpl extends ActionBar.Tab {
-        private Fragment mFragment;
+        private ActionBar.TabListener mCallback;
+        private Object mTag;
         private Drawable mIcon;
         private CharSequence mText;
         private int mPosition;
+        private View mCustomView;
 
         @Override
-        public Fragment getFragment() {
-            return mFragment;
+        public Object getTag() {
+            return mTag;
+        }
+
+        @Override
+        public void setTag(Object tag) {
+            mTag = tag;
+        }
+
+        public ActionBar.TabListener getCallback() {
+            return mCallback;
+        }
+
+        @Override
+        public void setTabListener(ActionBar.TabListener callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public View getCustomView() {
+            return mCustomView;
+        }
+
+        @Override
+        public void setCustomView(View view) {
+            mCustomView = view;
         }
 
         @Override
@@ -572,11 +550,6 @@
         }
 
         @Override
-        public void setFragment(Fragment fragment) {
-            mFragment = fragment;
-        }
-
-        @Override
         public void setIcon(Drawable icon) {
             mIcon = icon;
         }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 0a1c8ff..9ed3658 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -22,6 +22,8 @@
 import android.net.TrafficStats;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Message;
 import android.os.Parcel;
 import android.os.ParcelFormatException;
 import android.os.Parcelable;
@@ -79,6 +81,38 @@
 
     private final JournaledFile mFile;
 
+    static final int MSG_UPDATE_WAKELOCKS = 1;
+    static final int MSG_REPORT_POWER_CHANGE = 2;
+    static final long DELAY_UPDATE_WAKELOCKS = 15*1000;
+
+    public interface BatteryCallback {
+        public void batteryNeedsCpuUpdate();
+        public void batteryPowerChanged(boolean onBattery);
+    }
+
+    final class MyHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            BatteryCallback cb = mCallback;
+            switch (msg.what) {
+                case MSG_UPDATE_WAKELOCKS:
+                    if (cb != null) {
+                        cb.batteryNeedsCpuUpdate();
+                    }
+                    break;
+                case MSG_REPORT_POWER_CHANGE:
+                    if (cb != null) {
+                        cb.batteryPowerChanged(msg.arg1 != 0);
+                    }
+                    break;
+            }
+        }
+    }
+
+    private final MyHandler mHandler;
+
+    private BatteryCallback mCallback;
+
     /**
      * The statistics we have collected organized by uids.
      */
@@ -95,6 +129,9 @@
     final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers
             = new SparseArray<ArrayList<StopwatchTimer>>();
 
+    // Last partial timers we use for distributing CPU usage.
+    final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>();
+
     // These are the objects that will want to do something when the device
     // is unplugged from power.
     final ArrayList<Unpluggable> mUnpluggables = new ArrayList<Unpluggable>();
@@ -240,6 +277,7 @@
     // For debugging
     public BatteryStatsImpl() {
         mFile = null;
+        mHandler = null;
     }
 
     public static interface Unpluggable {
@@ -739,7 +777,9 @@
      * State for keeping track of timing information.
      */
     public static final class StopwatchTimer extends Timer {
+        final Uid mUid;
         final ArrayList<StopwatchTimer> mTimerPool;
+
         int mNesting;
 
         /**
@@ -757,16 +797,24 @@
 
         long mTimeout;
 
-        StopwatchTimer(int type, ArrayList<StopwatchTimer> timerPool,
+        /**
+         * For partial wake locks, keep track of whether we are in the list
+         * to consume CPU cycles.
+         */
+        boolean mInList;
+
+        StopwatchTimer(Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
                 ArrayList<Unpluggable> unpluggables, Parcel in) {
             super(type, unpluggables, in);
+            mUid = uid;
             mTimerPool = timerPool;
             mUpdateTime = in.readLong();
         }
 
-        StopwatchTimer(int type, ArrayList<StopwatchTimer> timerPool,
+        StopwatchTimer(Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
                 ArrayList<Unpluggable> unpluggables) {
             super(type, unpluggables);
+            mUid = uid;
             mTimerPool = timerPool;
         }
         
@@ -1254,6 +1302,10 @@
             mWakeLockNesting++;
         }
         if (uid >= 0) {
+            if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
+                Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
+                mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS);
+            }
             getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type);
         }
     }
@@ -1269,10 +1321,112 @@
             }
         }
         if (uid >= 0) {
+            if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
+                Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
+                mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS);
+            }
             getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type);
         }
     }
 
+    public int startAddingCpuLocked() {
+        mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
+
+        if (mScreenOn) {
+            return 0;
+        }
+
+        final int N = mPartialTimers.size();
+        if (N == 0) {
+            mLastPartialTimers.clear();
+            return 0;
+        }
+
+        // How many timers should consume CPU?  Only want to include ones
+        // that have already been in the list.
+        for (int i=0; i<N; i++) {
+            StopwatchTimer st = mPartialTimers.get(i);
+            if (st.mInList) {
+                Uid uid = st.mUid;
+                // We don't include the system UID, because it so often
+                // holds wake locks at one request or another of an app.
+                if (uid != null && uid.mUid != Process.SYSTEM_UID) {
+                    return 50;
+                }
+            }
+        }
+
+        return 0;
+    }
+
+    public void finishAddingCpuLocked(int perc, int utime, int stime, long[] cpuSpeedTimes) {
+        final int N = mPartialTimers.size();
+        if (perc != 0) {
+            int num = 0;
+            for (int i=0; i<N; i++) {
+                StopwatchTimer st = mPartialTimers.get(i);
+                if (st.mInList) {
+                    Uid uid = st.mUid;
+                    // We don't include the system UID, because it so often
+                    // holds wake locks at one request or another of an app.
+                    if (uid != null && uid.mUid != Process.SYSTEM_UID) {
+                        num++;
+                    }
+                }
+            }
+            if (num != 0) {
+                for (int i=0; i<N; i++) {
+                    StopwatchTimer st = mPartialTimers.get(i);
+                    if (st.mInList) {
+                        int myUTime = utime/num;
+                        int mySTime = stime/num;
+                        utime -= myUTime;
+                        stime -= mySTime;
+                        num--;
+                        Uid uid = st.mUid;
+                        if (uid != null && uid.mUid != Process.SYSTEM_UID) {
+                            Uid.Proc proc = uid.getProcessStatsLocked("*wakelock*");
+                            proc.addCpuTimeLocked(myUTime, mySTime);
+                            proc.addSpeedStepTimes(cpuSpeedTimes);
+                        }
+                    }
+                }
+            }
+
+            // Just in case, collect any lost CPU time.
+            if (utime != 0 || stime != 0) {
+                Uid uid = getUidStatsLocked(Process.SYSTEM_UID);
+                if (uid != null) {
+                    Uid.Proc proc = uid.getProcessStatsLocked("*lost*");
+                    proc.addCpuTimeLocked(utime, stime);
+                    proc.addSpeedStepTimes(cpuSpeedTimes);
+                }
+            }
+        }
+
+        final int NL = mLastPartialTimers.size();
+        boolean diff = N != NL;
+        for (int i=0; i<NL && !diff; i++) {
+            diff |= mPartialTimers.get(i) != mLastPartialTimers.get(i);
+        }
+        if (!diff) {
+            for (int i=0; i<NL; i++) {
+                mPartialTimers.get(i).mInList = true;
+            }
+            return;
+        }
+
+        for (int i=0; i<NL; i++) {
+            mLastPartialTimers.get(i).mInList = false;
+        }
+        mLastPartialTimers.clear();
+        for (int i=0; i<N; i++) {
+            StopwatchTimer st = mPartialTimers.get(i);
+            st.mInList = true;
+            mLastPartialTimers.add(st);
+        }
+    }
+
     public void noteProcessDiedLocked(int uid, int pid) {
         Uid u = mUidStats.get(uid);
         if (u != null) {
@@ -1924,13 +2078,18 @@
 
         public Uid(int uid) {
             mUid = uid;
-            mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables);
-            mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK, null, mUnpluggables);
-            mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK, null, mUnpluggables);
-            mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
+            mWifiTurnedOnTimer = new StopwatchTimer(Uid.this, WIFI_TURNED_ON,
                     null, mUnpluggables);
-            mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON, null, mUnpluggables);
-            mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON, null, mUnpluggables);
+            mFullWifiLockTimer = new StopwatchTimer(Uid.this, FULL_WIFI_LOCK,
+                    null, mUnpluggables);
+            mScanWifiLockTimer = new StopwatchTimer(Uid.this, SCAN_WIFI_LOCK,
+                    null, mUnpluggables);
+            mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
+                    null, mUnpluggables);
+            mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
+                    null, mUnpluggables);
+            mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
+                    null, mUnpluggables);
         }
 
         @Override
@@ -1998,7 +2157,7 @@
             if (!mWifiTurnedOn) {
                 mWifiTurnedOn = true;
                 if (mWifiTurnedOnTimer == null) {
-                    mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON,
+                    mWifiTurnedOnTimer = new StopwatchTimer(Uid.this, WIFI_TURNED_ON,
                             null, mUnpluggables);
                 }
                 mWifiTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2018,7 +2177,7 @@
             if (!mFullWifiLockOut) {
                 mFullWifiLockOut = true;
                 if (mFullWifiLockTimer == null) {
-                    mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK,
+                    mFullWifiLockTimer = new StopwatchTimer(Uid.this, FULL_WIFI_LOCK,
                             null, mUnpluggables);
                 }
                 mFullWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2038,7 +2197,7 @@
             if (!mScanWifiLockOut) {
                 mScanWifiLockOut = true;
                 if (mScanWifiLockTimer == null) {
-                    mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK,
+                    mScanWifiLockTimer = new StopwatchTimer(Uid.this, SCAN_WIFI_LOCK,
                             null, mUnpluggables);
                 }
                 mScanWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2058,7 +2217,7 @@
             if (!mWifiMulticastEnabled) {
                 mWifiMulticastEnabled = true;
                 if (mWifiMulticastTimer == null) {
-                    mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
+                    mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
                             null, mUnpluggables);
                 }
                 mWifiMulticastTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2078,7 +2237,7 @@
             if (!mAudioTurnedOn) {
                 mAudioTurnedOn = true;
                 if (mAudioTurnedOnTimer == null) {
-                    mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON,
+                    mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
                             null, mUnpluggables);
                 }
                 mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2098,7 +2257,7 @@
             if (!mVideoTurnedOn) {
                 mVideoTurnedOn = true;
                 if (mVideoTurnedOnTimer == null) {
-                    mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON,
+                    mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
                             null, mUnpluggables);
                 }
                 mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2458,42 +2617,42 @@
             mTcpBytesSentAtLastUnplug = in.readLong();
             mWifiTurnedOn = false;
             if (in.readInt() != 0) {
-                mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON,
+                mWifiTurnedOnTimer = new StopwatchTimer(Uid.this, WIFI_TURNED_ON,
                         null, mUnpluggables, in);
             } else {
                 mWifiTurnedOnTimer = null;
             }
             mFullWifiLockOut = false;
             if (in.readInt() != 0) {
-                mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK,
+                mFullWifiLockTimer = new StopwatchTimer(Uid.this, FULL_WIFI_LOCK,
                         null, mUnpluggables, in);
             } else {
                 mFullWifiLockTimer = null;
             }
             mScanWifiLockOut = false;
             if (in.readInt() != 0) {
-                mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK,
+                mScanWifiLockTimer = new StopwatchTimer(Uid.this, SCAN_WIFI_LOCK,
                         null, mUnpluggables, in);
             } else {
                 mScanWifiLockTimer = null;
             }
             mWifiMulticastEnabled = false;
             if (in.readInt() != 0) {
-                mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
+                mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
                         null, mUnpluggables, in);
             } else {
                 mWifiMulticastTimer = null;
             }
             mAudioTurnedOn = false;
             if (in.readInt() != 0) {
-                mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON,
+                mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
                         null, mUnpluggables, in);
             } else {
                 mAudioTurnedOnTimer = null;
             }
             mVideoTurnedOn = false;
             if (in.readInt() != 0) {
-                mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON,
+                mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
                         null, mUnpluggables, in);
             } else {
                 mVideoTurnedOnTimer = null;
@@ -2540,7 +2699,7 @@
                     return null;
                 }
 
-                return new StopwatchTimer(type, pool, unpluggables, in);
+                return new StopwatchTimer(Uid.this, type, pool, unpluggables, in);
             }
 
             boolean reset() {
@@ -2616,7 +2775,7 @@
                     pool = new ArrayList<StopwatchTimer>();
                     mSensorTimers.put(mHandle, pool);
                 }
-                return new StopwatchTimer(0, pool, unpluggables, in);
+                return new StopwatchTimer(Uid.this, 0, pool, unpluggables, in);
             }
 
             boolean reset() {
@@ -3345,11 +3504,6 @@
             }
         }
 
-        public class Pid {
-            long mWakeSum;
-            long mWakeStart;
-        }
-
         /**
          * Retrieve the statistics object for a particular process, creating
          * if needed.
@@ -3364,6 +3518,10 @@
             return ps;
         }
 
+        public SparseArray<? extends Pid> getPidStats() {
+            return mPids;
+        }
+        
         public Pid getPidStatsLocked(int pid) {
             Pid p = mPids.get(pid);
             if (p == null) {
@@ -3419,21 +3577,24 @@
                 case WAKE_TYPE_PARTIAL:
                     t = wl.mTimerPartial;
                     if (t == null) {
-                        t = new StopwatchTimer(WAKE_TYPE_PARTIAL, mPartialTimers, mUnpluggables);
+                        t = new StopwatchTimer(Uid.this, WAKE_TYPE_PARTIAL,
+                                mPartialTimers, mUnpluggables);
                         wl.mTimerPartial = t;
                     }
                     return t;
                 case WAKE_TYPE_FULL:
                     t = wl.mTimerFull;
                     if (t == null) {
-                        t = new StopwatchTimer(WAKE_TYPE_FULL, mFullTimers, mUnpluggables);
+                        t = new StopwatchTimer(Uid.this, WAKE_TYPE_FULL,
+                                mFullTimers, mUnpluggables);
                         wl.mTimerFull = t;
                     }
                     return t;
                 case WAKE_TYPE_WINDOW:
                     t = wl.mTimerWindow;
                     if (t == null) {
-                        t = new StopwatchTimer(WAKE_TYPE_WINDOW, mWindowTimers, mUnpluggables);
+                        t = new StopwatchTimer(Uid.this, WAKE_TYPE_WINDOW,
+                                mWindowTimers, mUnpluggables);
                         wl.mTimerWindow = t;
                     }
                     return t;
@@ -3460,7 +3621,7 @@
                 timers = new ArrayList<StopwatchTimer>();
                 mSensorTimers.put(sensor, timers);
             }
-            t = new StopwatchTimer(BatteryStats.SENSOR, timers, mUnpluggables);
+            t = new StopwatchTimer(Uid.this, BatteryStats.SENSOR, timers, mUnpluggables);
             se.mTimer = t;
             return t;
         }
@@ -3533,25 +3694,26 @@
 
     public BatteryStatsImpl(String filename) {
         mFile = new JournaledFile(new File(filename), new File(filename + ".tmp"));
+        mHandler = new MyHandler();
         mStartCount++;
-        mScreenOnTimer = new StopwatchTimer(-1, null, mUnpluggables);
+        mScreenOnTimer = new StopwatchTimer(null, -1, null, mUnpluggables);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
-            mScreenBrightnessTimer[i] = new StopwatchTimer(-100-i, null, mUnpluggables);
+            mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mUnpluggables);
         }
         mInputEventCounter = new Counter(mUnpluggables);
-        mPhoneOnTimer = new StopwatchTimer(-2, null, mUnpluggables);
+        mPhoneOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables);
         for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
-            mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(-200-i, null, mUnpluggables);
+            mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null, mUnpluggables);
         }
-        mPhoneSignalScanningTimer = new StopwatchTimer(-200+1, null, mUnpluggables);
+        mPhoneSignalScanningTimer = new StopwatchTimer(null, -200+1, null, mUnpluggables);
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
-            mPhoneDataConnectionsTimer[i] = new StopwatchTimer(-300-i, null, mUnpluggables);
+            mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i, null, mUnpluggables);
         }
-        mWifiOnTimer = new StopwatchTimer(-3, null, mUnpluggables);
-        mWifiRunningTimer = new StopwatchTimer(-4, null, mUnpluggables);
-        mBluetoothOnTimer = new StopwatchTimer(-5, null, mUnpluggables);
-        mAudioOnTimer = new StopwatchTimer(-6, null, mUnpluggables);
-        mVideoOnTimer = new StopwatchTimer(-7, null, mUnpluggables);
+        mWifiOnTimer = new StopwatchTimer(null, -3, null, mUnpluggables);
+        mWifiRunningTimer = new StopwatchTimer(null, -4, null, mUnpluggables);
+        mBluetoothOnTimer = new StopwatchTimer(null, -5, null, mUnpluggables);
+        mAudioOnTimer = new StopwatchTimer(null, -6, null, mUnpluggables);
+        mVideoOnTimer = new StopwatchTimer(null, -7, null, mUnpluggables);
         mOnBattery = mOnBatteryInternal = false;
         initTimes();
         mTrackBatteryPastUptime = 0;
@@ -3569,9 +3731,14 @@
 
     public BatteryStatsImpl(Parcel p) {
         mFile = null;
+        mHandler = null;
         readFromParcel(p);
     }
 
+    public void setCallback(BatteryCallback cb) {
+        mCallback = cb;
+    }
+
     public void setNumSpeedSteps(int steps) {
         if (sNumSpeedSteps == 0) sNumSpeedSteps = steps;
     }
@@ -3588,6 +3755,11 @@
     }
     
     @Override
+    public long getHistoryBaseTime() {
+        return mHistoryBaseTime;
+    }
+    
+    @Override
     public int getStartCount() {
         return mStartCount;
     }
@@ -3651,6 +3823,9 @@
     void setOnBattery(boolean onBattery, int oldStatus, int level) {
         synchronized(this) {
             boolean doWrite = false;
+            Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
+            m.arg1 = onBattery ? 1 : 0;
+            mHandler.sendMessage(m);
             mOnBattery = mOnBatteryInternal = onBattery;
             
             long uptime = SystemClock.uptimeMillis() * 1000;
@@ -4551,26 +4726,29 @@
         mBatteryRealtime = in.readLong();
         mBatteryLastRealtime = 0;
         mScreenOn = false;
-        mScreenOnTimer = new StopwatchTimer(-1, null, mUnpluggables, in);
+        mScreenOnTimer = new StopwatchTimer(null, -1, null, mUnpluggables, in);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
-            mScreenBrightnessTimer[i] = new StopwatchTimer(-100-i, null, mUnpluggables, in);
+            mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i,
+                    null, mUnpluggables, in);
         }
         mInputEventCounter = new Counter(mUnpluggables, in);
         mPhoneOn = false;
-        mPhoneOnTimer = new StopwatchTimer(-2, null, mUnpluggables, in);
+        mPhoneOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
         for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
-            mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(-200-i, null, mUnpluggables, in);
+            mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i,
+                    null, mUnpluggables, in);
         }
-        mPhoneSignalScanningTimer = new StopwatchTimer(-200+1, null, mUnpluggables, in);
+        mPhoneSignalScanningTimer = new StopwatchTimer(null, -200+1, null, mUnpluggables, in);
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
-            mPhoneDataConnectionsTimer[i] = new StopwatchTimer(-300-i, null, mUnpluggables, in);
+            mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i,
+                    null, mUnpluggables, in);
         }
         mWifiOn = false;
-        mWifiOnTimer = new StopwatchTimer(-2, null, mUnpluggables, in);
+        mWifiOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
         mWifiRunning = false;
-        mWifiRunningTimer = new StopwatchTimer(-2, null, mUnpluggables, in);
+        mWifiRunningTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
         mBluetoothOn = false;
-        mBluetoothOnTimer = new StopwatchTimer(-2, null, mUnpluggables, in);
+        mBluetoothOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
         mUptime = in.readLong();
         mUptimeStart = in.readLong();
         mLastUptime = 0;
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index a221faf..af0438c6 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -75,6 +75,13 @@
         setEnabled(itemData.isEnabled());
     }
 
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        mImageButton.setEnabled(enabled);
+        mTextButton.setEnabled(enabled);
+    }
+
     public void onClick(View v) {
         if (mItemInvoker != null) {
             mItemInvoker.invokeItem(mItemData);
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index b9e4e46..6b3d353 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -41,6 +41,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
+import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.Spinner;
@@ -87,6 +88,7 @@
     private TextView mTitleView;
     private TextView mSubtitleView;
     private Spinner mSpinner;
+    private HorizontalScrollView mTabScrollView;
     private LinearLayout mTabLayout;
     private View mCustomNavView;
     
@@ -119,6 +121,8 @@
 
     private OnClickListener mHomeClickListener = null;
 
+    private OnClickListener mTabClickListener = null;
+
     public ActionBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -346,8 +350,9 @@
                 break;
             case ActionBar.NAVIGATION_MODE_TABS:
                 if (mTabLayout != null) {
-                    removeView(mTabLayout);
+                    removeView(mTabScrollView);
                     mTabLayout = null;
+                    mTabScrollView = null;
                 }
             }
             
@@ -365,8 +370,10 @@
                 addView(mCustomNavView);
                 break;
             case ActionBar.NAVIGATION_MODE_TABS:
+                mTabScrollView = new HorizontalScrollView(getContext());
                 mTabLayout = new LinearLayout(getContext());
-                addView(mTabLayout);
+                mTabScrollView.addView(mTabLayout);
+                addView(mTabScrollView);
                 break;
             }
             mNavigationMode = mode;
@@ -401,20 +408,24 @@
     private TabView createTabView(ActionBar.Tab tab) {
         final TabView tabView = new TabView(getContext(), tab);
         tabView.setFocusable(true);
-        tabView.setOnClickListener(new TabClickListener());
+
+        if (mTabClickListener == null) {
+            mTabClickListener = new TabClickListener();
+        }
+        tabView.setOnClickListener(mTabClickListener);
         return tabView;
     }
 
     public void addTab(ActionBar.Tab tab) {
         final boolean isFirst = mTabLayout.getChildCount() == 0;
-        final TabView tabView = createTabView(tab);
+        View tabView = createTabView(tab);
         mTabLayout.addView(tabView);
         if (isFirst) {
             tabView.setSelected(true);
         }
     }
 
-    public void insertTab(ActionBar.Tab tab, int position) {
+    public void addTab(ActionBar.Tab tab, int position) {
         final boolean isFirst = mTabLayout.getChildCount() == 0;
         final TabView tabView = createTabView(tab);
         mTabLayout.addView(tabView, position);
@@ -595,8 +606,8 @@
             }
             break;
         case ActionBar.NAVIGATION_MODE_TABS:
-            if (mTabLayout != null) {
-                mTabLayout.measure(
+            if (mTabScrollView != null) {
+                mTabScrollView.measure(
                         MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
                         MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
             }
@@ -663,8 +674,8 @@
             }
             break;
         case ActionBar.NAVIGATION_MODE_TABS:
-            if (mTabLayout != null) {
-                x += positionChild(mTabLayout, x, y, contentHeight) + mSpacing;
+            if (mTabScrollView != null) {
+                x += positionChild(mTabScrollView, x, y, contentHeight) + mSpacing;
             }
         }
 
@@ -703,31 +714,36 @@
             super(context);
             mTab = tab;
 
-            // TODO Style tabs based on the theme
+            final View custom = tab.getCustomView();
+            if (custom != null) {
+                addView(custom);
+            } else {
+                // TODO Style tabs based on the theme
 
-            final Drawable icon = tab.getIcon();
-            final CharSequence text = tab.getText();
+                final Drawable icon = tab.getIcon();
+                final CharSequence text = tab.getText();
 
-            if (icon != null) {
-                ImageView iconView = new ImageView(context);
-                iconView.setImageDrawable(icon);
-                LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                        LayoutParams.WRAP_CONTENT);
-                lp.gravity = Gravity.CENTER_VERTICAL;
-                iconView.setLayoutParams(lp);
-                addView(iconView);
-            }
+                if (icon != null) {
+                    ImageView iconView = new ImageView(context);
+                    iconView.setImageDrawable(icon);
+                    LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                            LayoutParams.WRAP_CONTENT);
+                    lp.gravity = Gravity.CENTER_VERTICAL;
+                    iconView.setLayoutParams(lp);
+                    addView(iconView);
+                }
 
-            if (text != null) {
-                TextView textView = new TextView(context);
-                textView.setText(text);
-                textView.setSingleLine();
-                textView.setEllipsize(TruncateAt.END);
-                LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                        LayoutParams.WRAP_CONTENT);
-                lp.gravity = Gravity.CENTER_VERTICAL;
-                textView.setLayoutParams(lp);
-                addView(textView);
+                if (text != null) {
+                    TextView textView = new TextView(context);
+                    textView.setText(text);
+                    textView.setSingleLine();
+                    textView.setEllipsize(TruncateAt.END);
+                    LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                            LayoutParams.WRAP_CONTENT);
+                    lp.gravity = Gravity.CENTER_VERTICAL;
+                    textView.setLayoutParams(lp);
+                    addView(textView);
+                }
             }
 
             setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
diff --git a/core/java/com/android/internal/widget/CarouselRS.java b/core/java/com/android/internal/widget/CarouselRS.java
deleted file mode 100644
index 7589531..0000000
--- a/core/java/com/android/internal/widget/CarouselRS.java
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * 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.widget;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.renderscript.*;
-import android.renderscript.RenderScript.RSMessage;
-import android.renderscript.Sampler.Value;
-import android.renderscript.ProgramRaster.CullMode;
-import android.util.Log;
-
-import com.android.internal.R;
-
-import static android.renderscript.Element.*;
-import static android.renderscript.Sampler.Value.LINEAR;
-import static android.renderscript.Sampler.Value.WRAP;
-import static android.renderscript.Sampler.Value.CLAMP;
-
-public class CarouselRS  {
-    private static final int DEFAULT_VISIBLE_SLOTS = 1;
-    private static final int DEFAULT_CARD_COUNT = 1;
-    
-    // Client messages *** THIS LIST MUST MATCH THOSE IN carousel.rs ***
-    public static final int CMD_CARD_SELECTED = 100;
-    public static final int CMD_REQUEST_TEXTURE = 200;
-    public static final int CMD_INVALIDATE_TEXTURE = 210;
-    public static final int CMD_REQUEST_GEOMETRY = 300;
-    public static final int CMD_INVALIDATE_GEOMETRY = 310;
-    public static final int CMD_ANIMATION_STARTED = 400;
-    public static final int CMD_ANIMATION_FINISHED = 500;
-    public static final int CMD_PING = 600; // for debugging
-    
-    private static final String TAG = "CarouselRS";
-    private static final int DEFAULT_SLOT_COUNT = 10;
-    private static final boolean MIPMAP = false;
-    
-    private RenderScriptGL mRS;
-    private Resources mRes;
-    private ScriptC_Carousel mScript;
-    private ScriptField_Card mCards;
-    private Sampler mSampler;
-    private ProgramRaster mProgramRaster;
-    private ProgramStore mProgramStore;
-    private ProgramFragment mFragmentProgram;
-    private ProgramVertex mVertexProgram;
-    private ProgramRaster mRasterProgram;
-    private CarouselCallback mCallback;
-    private float[] mEyePoint = new float[3];
-    private float[] mAtPoint = new float[3];
-    private float[] mUp = new float[3];
-    
-    public static interface CarouselCallback {
-        /**
-         * Called when a card is selected
-         * @param n the id of the card
-         */
-        void onCardSelected(int n);
-        
-        /**
-         * Called when texture is needed for card n.  This happens when the given card becomes
-         * visible.
-         * @param n the id of the card
-         */
-        void onRequestTexture(int n);
-        
-        /**
-         * Called when a texture is no longer needed for card n.  This happens when the card
-         * goes out of view.
-         * @param n the id of the card
-         */
-        void onInvalidateTexture(int n);
-        
-        /**
-         * Called when geometry is needed for card n.
-         * @param n the id of the card.
-         */
-        void onRequestGeometry(int n);
-        
-        /**
-         * Called when geometry is no longer needed for card n. This happens when the card goes 
-         * out of view.
-         * @param n the id of the card
-         */
-        void onInvalidateGeometry(int n);
-        
-        /**
-         * Called when card animation (e.g. a fling) has started.
-         */
-        void onAnimationStarted();
-        
-        /**
-         * Called when card animation has stopped.
-         */
-        void onAnimationFinished();
-    };
-    
-    private RSMessage mRsMessage = new RSMessage() {
-        public void run() {
-            if (mCallback == null) return;
-            switch (mID) {
-                case CMD_CARD_SELECTED:
-                    mCallback.onCardSelected(mData[0]);
-                    break;
-                    
-                case CMD_REQUEST_TEXTURE:
-                    mCallback.onRequestTexture(mData[0]);
-                    break;
-                   
-                case CMD_INVALIDATE_TEXTURE:
-                    mCallback.onInvalidateTexture(mData[0]);
-                    break;
-                    
-                case CMD_REQUEST_GEOMETRY:
-                    mCallback.onRequestGeometry(mData[0]);
-                    break;
-                    
-                case CMD_INVALIDATE_GEOMETRY:
-                    mCallback.onInvalidateGeometry(mData[0]);
-                    break;
-                    
-                case CMD_ANIMATION_STARTED:
-                    mCallback.onAnimationStarted();
-                    break;
-                    
-                case CMD_ANIMATION_FINISHED:
-                    mCallback.onAnimationFinished();
-                    break;
-                    
-                case CMD_PING:
-                    Log.v(TAG, "PING...");
-                    break;
-                    
-                default:
-                    Log.e(TAG, "Unknown RSMessage: " + mID);
-            }
-        }
-    };
-    
-    public void init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-
-        // create the script object
-        mScript = new ScriptC_Carousel(mRS, mRes, R.raw.carousel, true);
-        mRS.mMessageCallback = mRsMessage;
-
-        initProgramStore();
-        initFragmentProgram();
-        initRasterProgram();
-        initVertexProgram();
-        
-        setSlotCount(DEFAULT_SLOT_COUNT);
-        setVisibleSlots(DEFAULT_VISIBLE_SLOTS);
-        createCards(DEFAULT_CARD_COUNT);
-        
-        setStartAngle(0.0f);
-        setRadius(1.0f);
-        
-        // update the camera
-        boolean pcam = true;
-        if (pcam) {
-            float eye[] = { 20.6829f, 2.77081f, 16.7314f };
-            float at[] = { 14.7255f, -3.40001f, -1.30184f };
-            float up[] = { 0.0f, 1.0f, 0.0f };
-            setLookAt(eye, at, up);
-            setRadius(20.0f);
-            // Fov: 25
-        } else {
-            mScript.invoke_lookAt(2.5f, 2.0f, 2.5f, 0.0f, -0.75f, 0.0f,  0.0f, 1.0f, 0.0f);
-            mScript.set_cardRotation(0.0f);
-            setRadius(1.5f);
-        }
-
-        resumeRendering();
-    }
-
-    public void setLookAt(float[] eye, float[] at, float[] up) {
-        for (int i = 0; i < 3; i++) {
-            mEyePoint[i] = eye[i];
-            mAtPoint[i] = at[i];
-            mUp[i] = up[i];
-        }
-        mScript.invoke_lookAt(eye[0], eye[1], eye[2], at[0], at[1], at[2], up[0], up[1], up[2]);
-    }
-
-    public void setRadius(float radius) {
-        mScript.set_radius(radius);
-    }
-
-    private void initVertexProgram() {
-        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
-        mVertexProgram = pvb.create();
-        ProgramVertex.MatrixAllocation pva = new ProgramVertex.MatrixAllocation(mRS);
-        mVertexProgram.bindAllocation(pva);
-        pva.setupProjectionNormalized(1, 1);
-        mScript.set_vertexProgram(mVertexProgram);
-    }
-
-    private void initRasterProgram() {
-        ProgramRaster.Builder programRasterBuilder = new ProgramRaster.Builder(mRS);
-        mRasterProgram = programRasterBuilder.create();
-        //mRasterProgram.setCullMode(CullMode.NONE);
-        mScript.set_rasterProgram(mRasterProgram);
-    }
-
-    private void initFragmentProgram() {
-        Sampler.Builder sampleBuilder = new Sampler.Builder(mRS);
-        sampleBuilder.setMin(Value.LINEAR_MIP_LINEAR);
-        sampleBuilder.setMag(LINEAR);
-        sampleBuilder.setWrapS(CLAMP);
-        sampleBuilder.setWrapT(CLAMP);
-        mSampler = sampleBuilder.create();
-        ProgramFragment.Builder fragmentBuilder = new ProgramFragment.Builder(mRS);
-        fragmentBuilder.setTexture(ProgramFragment.Builder.EnvMode.DECAL,
-                           ProgramFragment.Builder.Format.RGBA, 0);
-        mFragmentProgram = fragmentBuilder.create();
-        mFragmentProgram.bindSampler(mSampler, 0);
-        mScript.set_fragmentProgram(mFragmentProgram);
-    }
-
-    private void initProgramStore() {
-        ProgramStore.Builder programStoreBuilder = new ProgramStore.Builder(mRS, null, null);
-        programStoreBuilder.setDepthFunc(ProgramStore.DepthFunc.LESS);
-        programStoreBuilder.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA, 
-                ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
-        programStoreBuilder.setDitherEnable(false);
-        programStoreBuilder.setDepthMask(true);
-        mProgramStore = programStoreBuilder.create();
-        mScript.set_programStore(mProgramStore);
-    }
-    
-    public void createCards(int count)
-    {
-        mCards = count > 0 ? new ScriptField_Card(mRS, count) : null;
-        mScript.bind_cards(mCards);
-        mScript.invoke_createCards(count);
-    }
-    
-    public void setVisibleSlots(int count)
-    {
-        mScript.set_visibleSlotCount(count);
-    }
-    
-    public void setDefaultBitmap(Bitmap bitmap)
-    {
-        mScript.set_defaultTexture(allocationFromBitmap(bitmap, MIPMAP));
-    }
-    
-    public void setLoadingBitmap(Bitmap bitmap)
-    {
-        mScript.set_loadingTexture(allocationFromBitmap(bitmap, MIPMAP));
-    }
-    
-    public void setDefaultGeometry(Mesh mesh)
-    {
-        mScript.set_defaultGeometry(mesh);
-    }
-    
-    public void setLoadingGeometry(Mesh mesh)
-    {
-        mScript.set_loadingGeometry(mesh);
-    }
-    
-    public void setStartAngle(float theta)
-    {
-        mScript.set_startAngle(theta);
-    }
-    
-    public void setCallback(CarouselCallback callback)
-    {
-        mCallback = callback;
-    }
-    
-    private Allocation allocationFromBitmap(Bitmap bitmap, boolean mipmap)
-    {
-        if (bitmap == null) return null;
-        Allocation allocation = Allocation.createFromBitmap(mRS, bitmap, RGB_565(mRS), mipmap);
-        allocation.uploadToTexture(0);
-        return allocation;
-    }
-    
-    public void setTexture(int n, Bitmap bitmap)
-    {
-        ScriptField_Card.Item item = mCards.get(n);
-        if (item == null) {
-            Log.v(TAG, "setTexture(): no item at index " + n);
-            item = new ScriptField_Card.Item();
-        }
-        if (bitmap != null) {
-            Log.v(TAG, "creating new bitmap");
-            item.texture = Allocation.createFromBitmap(mRS, bitmap, RGB_565(mRS), MIPMAP);
-            Log.v(TAG, "uploadToTexture(" + n + ")");
-            item.texture.uploadToTexture(0);
-            Log.v(TAG, "done...");
-        } else {
-            if (item.texture != null) {
-                Log.v(TAG, "unloading texture " + n);
-                // Don't wait for GC to free native memory.
-                // Only works if textures are not shared.
-                item.texture.destroy(); 
-                item.texture = null;
-            }
-        }
-        mCards.set(item, n, false); // This is primarily used for reference counting.
-        mScript.invoke_setTexture(n, item.texture);
-    }
-    
-    public void setGeometry(int n, Mesh geometry)
-    {
-        final boolean mipmap = false;
-        ScriptField_Card.Item item = mCards.get(n);
-        if (item == null) {
-            Log.v(TAG, "setGeometry(): no item at index " + n);
-            item = new ScriptField_Card.Item();
-        }
-        if (geometry != null) {
-            item.geometry = geometry;
-        } else {
-            Log.v(TAG, "unloading geometry " + n);
-            if (item.geometry != null) {
-                // item.geometry.destroy(); 
-                item.geometry = null;
-            }
-        }
-        mCards.set(item, n, false);
-        mScript.invoke_setGeometry(n, item.geometry);
-    }
-
-    public void pauseRendering() {
-        // Used to update multiple states at once w/o redrawing for each.
-        mRS.contextBindRootScript(null);
-    }
-    
-    public void resumeRendering() {
-        mRS.contextBindRootScript(mScript);
-    }
-    
-    public void doMotion(float x, float y) {
-        mScript.invoke_doMotion(x,y);
-    }
-    
-    public void doSelection(float x, float y) {
-        mScript.invoke_doSelection(x, y);
-    }
-
-    public void doStart(float x, float y) {
-        mScript.invoke_doStart(x, y);
-    }
-
-    public void doStop(float x, float y) {
-        mScript.invoke_doStop(x, y);
-    }
-
-    public void setSlotCount(int n) {
-        mScript.set_slotCount(n);
-    }
-}
diff --git a/core/java/com/android/internal/widget/CarouselView.java b/core/java/com/android/internal/widget/CarouselView.java
deleted file mode 100644
index 217805b..0000000
--- a/core/java/com/android/internal/widget/CarouselView.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * 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.widget;
-
-import com.android.internal.R;
-import com.android.internal.widget.CarouselRS.CarouselCallback;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.Bitmap.Config;
-import android.renderscript.FileA3D;
-import android.renderscript.Mesh;
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.SurfaceHolder;
-
-public class CarouselView extends RSSurfaceView {
-    private static final boolean USE_DEPTH_BUFFER = true;
-    private final int DEFAULT_SLOT_COUNT = 10;
-    private final Bitmap DEFAULT_BITMAP = Bitmap.createBitmap(1, 1, Config.RGB_565);
-    private static final String TAG = "CarouselView";
-    private CarouselRS mRenderScript;
-    private RenderScriptGL mRS;
-    private Context mContext;
-    private boolean mTracking;
-    private Bitmap mDefaultBitmap;
-    private Bitmap mLoadingBitmap;
-    private Mesh mDefaultGeometry;
-    private Mesh mLoadingGeometry;
-    private int mCardCount = 0;
-    private int mVisibleSlots = 0;
-    private float mStartAngle;
-    private int mSlotCount = DEFAULT_SLOT_COUNT;
-
-    public CarouselView(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Constructor used when this widget is created from a layout file.
-     */
-    public CarouselView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mContext = context;
-        boolean useDepthBuffer = true;
-        mRS = createRenderScript(USE_DEPTH_BUFFER);
-        mRenderScript = new CarouselRS();
-        mRenderScript.init(mRS, getResources());
-        // TODO: add parameters to layout
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        //mRS.contextSetSurface(w, h, holder.getSurface());
-        mRenderScript.init(mRS, getResources());
-        setSlotCount(mSlotCount);
-        createCards(mCardCount);
-        setVisibleSlots(mVisibleSlots);
-        setCallback(mCarouselCallback);
-        setDefaultBitmap(mDefaultBitmap);
-        setLoadingBitmap(mLoadingBitmap);
-        setDefaultGeometry(mDefaultGeometry);
-        setLoadingGeometry(mLoadingGeometry);
-        setStartAngle(mStartAngle);
-    }
-
-    /**
-     * Loads geometry from a resource id.
-     * 
-     * @param resId
-     * @return the loaded mesh or null if it cannot be loaded
-     */
-    public Mesh loadGeometry(int resId) {
-        Resources res = mContext.getResources();
-        FileA3D model = FileA3D.createFromResource(mRS, res, resId);
-        FileA3D.IndexEntry entry = model.getIndexEntry(0);
-        if(entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
-            return null;
-        }
-        return (Mesh) entry.getObject();
-    }
-    
-    /**
-     * Load A3D file from resource.  If resId == 0, will clear geometry for this item.
-     * @param n
-     * @param resId
-     */
-    public void setGeometryForItem(int n, Mesh mesh) {
-        mRenderScript.setGeometry(n, mesh);
-    }
-    
-    public void setSlotCount(int n) {
-        mSlotCount = n;
-        if (mRenderScript != null) {
-            mRenderScript.setSlotCount(n);
-        }
-    }
-
-    public void setVisibleSlots(int n) {
-        mVisibleSlots = n;
-        if (mRenderScript != null) {
-            mRenderScript.setVisibleSlots(n);
-        }
-    }
-
-    public void createCards(int n) {
-        mCardCount = n;
-        if (mRenderScript != null) {
-            mRenderScript.createCards(n);
-        }
-    }
-    
-    public void setTextureForItem(int n, Bitmap bitmap) {
-        if (mRenderScript != null) {
-            Log.v(TAG, "setTextureForItem(" + n + ")");
-            mRenderScript.setTexture(n, bitmap); 
-            Log.v(TAG, "done");
-        }
-    }
-
-    public void setDefaultBitmap(Bitmap bitmap) {
-        mDefaultBitmap = bitmap; 
-        if (mRenderScript != null) {
-            mRenderScript.setDefaultBitmap(bitmap);
-        }
-    }
-    
-    public void setLoadingBitmap(Bitmap bitmap) {
-        mLoadingBitmap = bitmap;
-        if (mRenderScript != null) {
-            mRenderScript.setLoadingBitmap(bitmap);
-        }
-    }
-    
-    public void setDefaultGeometry(Mesh mesh) {
-        mDefaultGeometry = mesh;
-        if (mRenderScript != null) {
-            mRenderScript.setDefaultGeometry(mesh);
-        }
-    }
-    
-    public void setLoadingGeometry(Mesh mesh) {
-        mLoadingGeometry = mesh;
-        if (mRenderScript != null) {
-            mRenderScript.setLoadingGeometry(mesh);
-        }
-    }
-    
-    public void setCallback(CarouselCallback callback)
-    {
-        mCarouselCallback = callback;
-        if (mRenderScript != null) {
-            mRenderScript.setCallback(callback);
-        }
-    }
-
-    public void setStartAngle(float angle)
-    {
-        mStartAngle = angle;
-        if (mRenderScript != null) {
-            mRenderScript.setStartAngle(angle);
-        }
-    }
-    
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if(mRS != null) {
-            mRS = null;
-            destroyRenderScript();
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (mRS == null) {
-            mRS = createRenderScript(USE_DEPTH_BUFFER);
-        }
-    }
-    
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        final int action = event.getAction();
-        final float x = event.getX();
-        final float y = event.getY();
-        
-        if (mRenderScript == null) {
-            return true;
-        }
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mTracking = true;
-                mRenderScript.doStart(x, y);
-                break;
-                
-            case MotionEvent.ACTION_MOVE:
-                if (mTracking) {
-                    mRenderScript.doMotion(x, y);
-                }
-                break;
-                
-            case MotionEvent.ACTION_UP:
-                mRenderScript.doStop(x, y);
-                mTracking = false;
-                break;
-        }
-
-        return true;
-    }
-    
-    private final CarouselCallback DEBUG_CALLBACK = new CarouselCallback() {
-        public void onAnimationStarted() {
-            Log.v(TAG, "onAnimationStarted()");
-        }
-        
-        public void onAnimationFinished() {
-            Log.v(TAG, "onAnimationFinished()");
-        }
-
-        public void onCardSelected(int n) {
-            Log.v(TAG, "onCardSelected(" + n + ")");
-        }
-
-        public void onRequestGeometry(int n) {
-            Log.v(TAG, "onRequestGeometry(" + n + ")");
-        }
-
-        public void onInvalidateGeometry(int n) {
-            Log.v(TAG, "onInvalidateGeometry(" + n + ")");
-        }
-        
-        public void onRequestTexture(final int n) {
-            Log.v(TAG, "onRequestTexture(" + n + ")");
-        }
-
-        public void onInvalidateTexture(int n) {
-            Log.v(TAG, "onInvalidateTexture(" + n + ")");
-        }
-
-    };
-    
-    private CarouselCallback mCarouselCallback = DEBUG_CALLBACK;
-}
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 4b56cb4..3865510 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -416,7 +416,7 @@
         }
 
         /**
-         * Start animating the slider. Note we need two animations since an Animator
+         * Start animating the slider. Note we need two animations since an ValueAnimator
          * keeps internal state of the invalidation region which is just the view being animated.
          *
          * @param anim1
diff --git a/core/java/com/android/internal/widget/carousel.rs b/core/java/com/android/internal/widget/carousel.rs
deleted file mode 100644
index 87e24c0..0000000
--- a/core/java/com/android/internal/widget/carousel.rs
+++ /dev/null
@@ -1,788 +0,0 @@
-/*
- * 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.
- */
-
-#pragma version(1)
-#pragma rs java_package_name(com.android.internal.widget)
-#pragma rs set_reflect_license()
-
-#include "rs_graphics.rsh"
-
-typedef struct __attribute__((aligned(4))) Card {
-    rs_allocation texture;
-    rs_mesh geometry;
-    //rs_matrix4x4 matrix; // custom transform for this card/geometry
-    int textureState;  // whether or not the texture is loaded.
-    int geometryState; // whether or not geometry is loaded
-    int visible; // not bool because of packing bug?
-} Card_t;
-
-typedef struct Ray_s {
-    float3 position;
-    float3 direction;
-} Ray;
-
-typedef struct PerspectiveCamera_s {
-    float3 from;
-    float3 at;
-    float3 up;
-    float  fov;
-    float  aspect;
-    float  near;
-    float  far;
-} PerspectiveCamera;
-
-// Request states. Used for loading 3D object properties from the Java client.
-// Typical properties: texture, geometry and matrices.
-enum {
-    STATE_INVALID = 0, // item hasn't been loaded
-    STATE_LOADING, // we've requested an item but are waiting for it to load
-    STATE_LOADED // item was delivered
-};
-
-// Client messages *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. ***
-static const int CMD_CARD_SELECTED = 100;
-static const int CMD_REQUEST_TEXTURE = 200;
-static const int CMD_INVALIDATE_TEXTURE = 210;
-static const int CMD_REQUEST_GEOMETRY = 300;
-static const int CMD_INVALIDATE_GEOMETRY = 310;
-static const int CMD_ANIMATION_STARTED = 400;
-static const int CMD_ANIMATION_FINISHED = 500;
-static const int CMD_PING = 600;
-
-// Constants
-static const int ANIMATION_SCALE_TIME = 200; // Time it takes to animate selected card, in ms
-static const float3 SELECTED_SCALE_FACTOR = { 0.2f, 0.2f, 0.2f }; // increase by this %
-
-// Debug flags
-bool debugCamera = false; // dumps ray/camera coordinate stuff
-bool debugPicking = false; // renders picking area on top of geometry
-
-// Exported variables. These will be reflected to Java set_* variables.
-Card_t *cards; // array of cards to draw
-float startAngle; // position of initial card, in radians
-int slotCount; // number of positions where a card can be
-int cardCount; // number of cards in stack
-int visibleSlotCount; // number of visible slots (for culling)
-float radius; // carousel radius. Cards will be centered on a circle with this radius
-float cardRotation; // rotation of card in XY plane relative to Z=1
-rs_program_store programStore;
-rs_program_fragment fragmentProgram;
-rs_program_vertex vertexProgram;
-rs_program_raster rasterProgram;
-rs_allocation defaultTexture; // shown when no other texture is assigned
-rs_allocation loadingTexture; // progress texture (shown when app is fetching the texture)
-rs_mesh defaultGeometry; // shown when no geometry is loaded
-rs_mesh loadingGeometry; // shown when geometry is loading
-rs_matrix4x4 projectionMatrix;
-rs_matrix4x4 modelviewMatrix;
-
-#pragma rs export_var(radius, cards, slotCount, visibleSlotCount, cardRotation)
-#pragma rs export_var(programStore, fragmentProgram, vertexProgram, rasterProgram)
-#pragma rs export_var(startAngle, defaultTexture, loadingTexture, defaultGeometry, loadingGeometry)
-#pragma rs export_func(createCards, lookAt, doStart, doStop, doMotion, doSelection, setTexture)
-#pragma rs export_func(setGeometry, debugCamera, debugPicking)
-
-// Local variables
-static float bias; // rotation bias, in radians. Used for animation and dragging.
-static bool updateCamera;    // force a recompute of projection and lookat matrices
-static bool initialized;
-static float3 backgroundColor = { 0.0f, 0.0f, 0.0f };
-static const float FLT_MAX = 1.0e37;
-static int currentSelection = -1;
-static int64_t touchTime = -1;  // time of first touch (see doStart())
-static float touchBias = 0.0f; // bias on first touch
-
-// Default geometry when card.geometry is not set.
-static const float3 cardVertices[4] = {
-        { -1.0, -1.0, 0.0 },
-        { 1.0, -1.0, 0.0 },
-        { 1.0, 1.0, 0.0 },
-        {-1.0, 1.0, 0.0 }
-};
-
-// Default camera
-static PerspectiveCamera camera = {
-        {2,2,2}, // from
-        {0,0,0}, // at
-        {0,1,0}, // up
-        25.0f,   // field of view
-        1.0f,    // aspect
-        0.1f,    // near
-        100.0f   // far
-};
-
-// Forward references
-static int intersectGeometry(Ray* ray, float *bestTime);
-static bool makeRayForPixelAt(Ray* ray, float x, float y);
-static float deltaTimeInSeconds(int64_t current);
-
-void init() {
-    // initializers currently have a problem when the variables are exported, so initialize
-    // globals here.
-    rsDebug("Renderscript: init()", 0);
-    startAngle = 0.0f;
-    slotCount = 10;
-    visibleSlotCount = 1;
-    bias = 0.0f;
-    radius = 1.0f;
-    cardRotation = 0.0f;
-    updateCamera = true;
-    initialized = false;
-}
-
-static void updateAllocationVars()
-{
-    // Cards
-    rs_allocation cardAlloc = rsGetAllocation(cards);
-    // TODO: use new rsIsObject()
-    cardCount = cardAlloc.p != 0 ? rsAllocationGetDimX(cardAlloc) : 0;
-}
-
-void createCards(int n)
-{
-    rsDebug("CreateCards: ", n);
-    initialized = false;
-    updateAllocationVars();
-}
-
-// Return angle for position p. Typically p will be an integer position, but can be fractional.
-static float cardPosition(float p)
-{
-    return startAngle + bias + 2.0f * M_PI * p / slotCount;
-}
-
-// Return slot for a card in position p. Typically p will be an integer slot, but can be fractional.
-static float slotPosition(float p)
-{
-    return startAngle + 2.0f * M_PI * p / slotCount;
-}
-
-// Return the lowest slot number for a given angular position.
-static int cardIndex(float angle)
-{
-    return floor(angle - startAngle - bias) * slotCount / (2.0f * M_PI);
-}
-
-// Set basic camera properties:
-//    from - position of the camera in x,y,z
-//    at - target we're looking at - used to compute view direction
-//    up - a normalized vector indicating up (typically { 0, 1, 0})
-//
-// NOTE: the view direction and up vector cannot be parallel/antiparallel with each other
-void lookAt(float fromX, float fromY, float fromZ,
-        float atX, float atY, float atZ,
-        float upX, float upY, float upZ)
-{
-    camera.from.x = fromX;
-    camera.from.y = fromY;
-    camera.from.z = fromZ;
-    camera.at.x = atX;
-    camera.at.y = atY;
-    camera.at.z = atZ;
-    camera.up.x = upX;
-    camera.up.y = upY;
-    camera.up.z = upZ;
-    updateCamera = true;
-}
-
-// Load a projection matrix for the given parameters.  This is equivalent to gluPerspective()
-static void loadPerspectiveMatrix(rs_matrix4x4* matrix, float fovy, float aspect, float near, float far)
-{
-    rsMatrixLoadIdentity(matrix);
-    float top = near * tan((float) (fovy * M_PI / 360.0f));
-    float bottom = -top;
-    float left = bottom * aspect;
-    float right = top * aspect;
-    rsMatrixLoadFrustum(matrix, left, right, bottom, top, near, far);
-}
-
-// Construct a matrix based on eye point, center and up direction. Based on the
-// man page for gluLookat(). Up must be normalized.
-static void loadLookatMatrix(rs_matrix4x4* matrix, float3 eye, float3 center, float3 up)
-{
-    float3 f = normalize(center - eye);
-    float3 s = normalize(cross(f, up));
-    float3 u = cross(s, f);
-    float m[16];
-    m[0] = s.x;
-    m[4] = s.y;
-    m[8] = s.z;
-    m[12] = 0.0f;
-    m[1] = u.x;
-    m[5] = u.y;
-    m[9] = u.z;
-    m[13] = 0.0f;
-    m[2] = -f.x;
-    m[6] = -f.y;
-    m[10] = -f.z;
-    m[14] = 0.0f;
-    m[3] = m[7] = m[11] = 0.0f;
-    m[15] = 1.0f;
-    rsMatrixLoad(matrix, m);
-    rsMatrixTranslate(matrix, -eye.x, -eye.y, -eye.z);
-}
-
-void setTexture(int n, rs_allocation texture)
-{
-    cards[n].texture = texture;
-    if (cards[n].texture.p != 0)
-        cards[n].textureState = STATE_LOADED;
-    else
-        cards[n].textureState = STATE_INVALID;
-}
-
-void setGeometry(int n, rs_mesh geometry)
-{
-    cards[n].geometry = geometry;
-    if (cards[n].geometry.p != 0)
-        cards[n].geometryState = STATE_LOADED;
-    else
-        cards[n].geometryState = STATE_INVALID;
-}
-
-static float3 getAnimatedScaleForSelected()
-{
-    int64_t dt = (rsUptimeMillis() - touchTime);
-    float fraction = (dt < ANIMATION_SCALE_TIME) ? (float) dt / ANIMATION_SCALE_TIME : 1.0f;
-    const float3 one = { 1.0f, 1.0f, 1.0f };
-    return one + fraction * SELECTED_SCALE_FACTOR;
-}
-
-static void getMatrixForCard(rs_matrix4x4* matrix, int i)
-{
-    float theta = cardPosition(i);
-    rsMatrixRotate(matrix, degrees(theta), 0, 1, 0);
-    rsMatrixTranslate(matrix, radius, 0, 0);
-    rsMatrixRotate(matrix, degrees(-theta + cardRotation), 0, 1, 0);
-    if (i == currentSelection) {
-        float3 scale = getAnimatedScaleForSelected();
-        rsMatrixScale(matrix, scale.x, scale.y, scale.z);
-    }
-    // TODO: apply custom matrix for cards[i].geometry
-}
-
-static void drawCards()
-{
-    float depth = 1.0f;
-    for (int i = 0; i < cardCount; i++) {
-        if (cards[i].visible) {
-            // Bind texture
-            if (cards[i].textureState == STATE_LOADED) {
-                rsgBindTexture(fragmentProgram, 0, cards[i].texture);
-            } else if (cards[i].textureState == STATE_LOADING) {
-                rsgBindTexture(fragmentProgram, 0, loadingTexture);
-            } else {
-                rsgBindTexture(fragmentProgram, 0, defaultTexture);
-            }
-
-            // Draw geometry
-            rs_matrix4x4 matrix = modelviewMatrix;
-            getMatrixForCard(&matrix, i);
-            rsgProgramVertexLoadModelMatrix(&matrix);
-            if (cards[i].geometryState == STATE_LOADED && cards[i].geometry.p != 0) {
-                rsgDrawMesh(cards[i].geometry);
-            } else if (cards[i].geometryState == STATE_LOADING && loadingGeometry.p != 0) {
-                rsgDrawMesh(loadingGeometry);
-            } else if (defaultGeometry.p != 0) {
-                rsgDrawMesh(defaultGeometry);
-            } else {
-                // Draw place-holder geometry
-                rsgDrawQuad(
-                    cardVertices[0].x, cardVertices[0].y, cardVertices[0].z,
-                    cardVertices[1].x, cardVertices[1].y, cardVertices[1].z,
-                    cardVertices[2].x, cardVertices[2].y, cardVertices[2].z,
-                    cardVertices[3].x, cardVertices[3].y, cardVertices[3].z);
-            }
-        }
-    }
-}
-
-static void updateCameraMatrix(float width, float height)
-{
-    float aspect = width / height;
-    if (aspect != camera.aspect || updateCamera) {
-        camera.aspect = aspect;
-        loadPerspectiveMatrix(&projectionMatrix, camera.fov, camera.aspect, camera.near, camera.far);
-        rsgProgramVertexLoadProjectionMatrix(&projectionMatrix);
-
-        loadLookatMatrix(&modelviewMatrix, camera.from, camera.at, camera.up);
-        rsgProgramVertexLoadModelMatrix(&modelviewMatrix);
-        updateCamera = false;
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Behavior/Physics
-////////////////////////////////////////////////////////////////////////////////////////////////////
-static float velocity = 0.0f;  // angular velocity in radians/s
-static bool isDragging;
-static int64_t lastTime = 0L; // keep track of how much time has passed between frames
-static float2 lastPosition;
-static bool animating = false;
-static float velocityThreshold = 0.1f * M_PI / 180.0f;
-static float velocityTracker;
-static int velocityTrackerCount;
-static float mass = 5.0f; // kg
-
-static const float G = 9.80f; // gravity constant, in m/s
-static const float springConstant = 0.0f;
-static const float frictionCoeff = 10.0f;
-static const float dragFactor = 0.25f;
-
-static float dragFunction(float x, float y)
-{
-    return dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI;
-}
-
-static float deltaTimeInSeconds(int64_t current)
-{
-    return (lastTime > 0L) ? (float) (current - lastTime) / 1000.0f : 0.0f;
-}
-
-int doSelection(float x, float y)
-{
-    Ray ray;
-    if (makeRayForPixelAt(&ray, x, y)) {
-        float bestTime = FLT_MAX;
-        return intersectGeometry(&ray, &bestTime);
-    }
-    return -1;
-}
-
-void doStart(float x, float y)
-{
-    lastPosition.x = x;
-    lastPosition.y = y;
-    velocity = 0.0f;
-    if (animating) {
-        rsSendToClient(CMD_ANIMATION_FINISHED);
-        animating = false;
-    }
-    velocityTracker = 0.0f;
-    velocityTrackerCount = 0;
-    touchTime = rsUptimeMillis();
-    touchBias = bias;
-    currentSelection = doSelection(x, y);
-}
-
-
-void doStop(float x, float y)
-{
-    int64_t currentTime = rsUptimeMillis();
-    updateAllocationVars();
-    if (currentSelection != -1 && (currentTime - touchTime) < ANIMATION_SCALE_TIME) {
-        rsDebug("HIT!", currentSelection);
-        int data[1];
-        data[0] = currentSelection;
-        rsSendToClientBlocking(CMD_CARD_SELECTED, data, sizeof(data));
-    } else {
-        velocity = velocityTrackerCount > 0 ?
-                    (velocityTracker / velocityTrackerCount) : 0.0f;  // avg velocity
-        if (fabs(velocity) > velocityThreshold) {
-            animating = true;
-            rsSendToClient(CMD_ANIMATION_STARTED);
-        }
-    }
-    currentSelection = -1;
-    lastTime = rsUptimeMillis();
-}
-
-void doMotion(float x, float y)
-{
-    int64_t currentTime = rsUptimeMillis();
-    float deltaOmega = dragFunction(x, y);
-    bias += deltaOmega;
-    lastPosition.x = x;
-    lastPosition.y = y;
-    float dt = deltaTimeInSeconds(currentTime);
-    if (dt > 0.0f) {
-        float v = deltaOmega / dt;
-        //if ((velocityTracker > 0.0f) == (v > 0.0f)) {
-            velocityTracker += v;
-            velocityTrackerCount++;
-        //} else {
-        //    velocityTracker = v;
-        //    velocityTrackerCount = 1;
-        //}
-    }
-
-    // Drop current selection if user drags position +- a partial slot
-    if (currentSelection != -1) {
-        const float slotMargin = 0.5f * (2.0f * M_PI / slotCount);
-        if (fabs(touchBias - bias) > slotMargin) {
-            currentSelection = -1;
-        }
-    }
-    lastTime = currentTime;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Hit detection using ray casting.
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-static bool
-rayTriangleIntersect(Ray* ray, float3 p0, float3 p1, float3 p2, float *tout)
-{
-    static const float tmin = 0.0f;
-
-    float3 e1 = p1 - p0;
-    float3 e2 = p2 - p0;
-    float3 s1 = cross(ray->direction, e2);
-
-    float div = dot(s1, e1);
-    if (div == 0.0f) return false;  // ray is parallel to plane.
-
-    float3 d = ray->position - p0;
-    float invDiv = 1.0f / div;
-
-    float u = dot(d, s1) * invDiv;
-    if (u < 0.0f || u > 1.0f) return false;
-
-    float3 s2 = cross(d, e1);
-    float v = dot(ray->direction, s2) * invDiv;
-    if ( v < 0.0f || (u+v) > 1.0f) return false;
-
-    float t = dot(e2, s2) * invDiv;
-    if (t < tmin || t > *tout)
-        return false;
-    *tout = t;
-    return true;
-}
-
-// Creates a ray for an Android pixel coordinate.
-// Note that the Y coordinate is opposite of GL rendering coordinates.
-static bool makeRayForPixelAt(Ray* ray, float x, float y)
-{
-    if (debugCamera) {
-        rsDebug("------ makeRay() -------", 0);
-        rsDebug("Camera.from:", camera.from);
-        rsDebug("Camera.at:", camera.at);
-        rsDebug("Camera.dir:", normalize(camera.at - camera.from));
-    }
-
-    // Vector math.  This has the potential to be much faster.
-    // TODO: pre-compute lowerLeftRay, du, dv to eliminate most of this math.
-    if (true) {
-        const float u = x / rsgGetWidth();
-        const float v = 1.0f - (y / rsgGetHeight());
-        const float aspect = (float) rsgGetWidth() / rsgGetHeight();
-        const float tanfov2 = 2.0f * tan(radians(camera.fov / 2.0f));
-        float3 dir = normalize(camera.at - camera.from);
-        float3 du = tanfov2 * normalize(cross(dir, camera.up));
-        float3 dv = tanfov2 * normalize(cross(du, dir));
-        du *= aspect;
-        float3 lowerLeftRay = dir - (0.5f * du) - (0.5f * dv);
-        const float3 rayPoint = camera.from;
-        const float3 rayDir = normalize(lowerLeftRay + u*du + v*dv);
-        if (debugCamera) {
-            rsDebug("Ray direction (vector math) = ", rayDir);
-        }
-
-        ray->position =  rayPoint;
-        ray->direction = rayDir;
-    }
-
-    // Matrix math.  This is more generic if we allow setting model view and projection matrices
-    // directly
-    else {
-        rs_matrix4x4 pm = modelviewMatrix;
-        rsMatrixLoadMultiply(&pm, &projectionMatrix, &modelviewMatrix);
-        if (!rsMatrixInverse(&pm)) {
-            rsDebug("ERROR: SINGULAR PM MATRIX", 0);
-            return false;
-        }
-        const float width = rsgGetWidth();
-        const float height = rsgGetHeight();
-        const float winx = 2.0f * x / width - 1.0f;
-        const float winy = 2.0f * y / height - 1.0f;
-
-        float4 eye = { 0.0f, 0.0f, 0.0f, 1.0f };
-        float4 at = { winx, winy, 1.0f, 1.0f };
-
-        eye = rsMatrixMultiply(&pm, eye);
-        eye *= 1.0f / eye.w;
-
-        at = rsMatrixMultiply(&pm, at);
-        at *= 1.0f / at.w;
-
-        const float3 rayPoint = { eye.x, eye.y, eye.z };
-        const float3 atPoint = { at.x, at.y, at.z };
-        const float3 rayDir = normalize(atPoint - rayPoint);
-        if (debugCamera) {
-            rsDebug("winx: ", winx);
-            rsDebug("winy: ", winy);
-            rsDebug("Ray position (transformed) = ", eye);
-            rsDebug("Ray direction (transformed) = ", rayDir);
-        }
-        ray->position =  rayPoint;
-        ray->direction = rayDir;
-    }
-
-    return true;
-}
-
-static int intersectGeometry(Ray* ray, float *bestTime)
-{
-    int hit = -1;
-    for (int id = 0; id < cardCount; id++) {
-        if (cards[id].visible) {
-            rs_matrix4x4 matrix;
-            float3 p[4];
-
-            // Transform card vertices to world space
-            rsMatrixLoadIdentity(&matrix);
-            getMatrixForCard(&matrix, id);
-            for (int vertex = 0; vertex < 4; vertex++) {
-                float4 tmp = rsMatrixMultiply(&matrix, cardVertices[vertex]);
-                if (tmp.w != 0.0f) {
-                    p[vertex].x = tmp.x;
-                    p[vertex].y = tmp.y;
-                    p[vertex].z = tmp.z;
-                    p[vertex] *= 1.0f / tmp.w;
-                } else {
-                    rsDebug("Bad w coord: ", tmp);
-                }
-            }
-
-            // Intersect card geometry
-            if (rayTriangleIntersect(ray, p[0], p[1], p[2], bestTime)
-                || rayTriangleIntersect(ray, p[2], p[3], p[0], bestTime)) {
-                hit = id;
-            }
-        }
-    }
-    return hit;
-}
-
-// This method computes the position of all the cards by updating bias based on a
-// simple physics model.
-// If the cards are still in motion, returns true.
-static bool updateNextPosition(int64_t currentTime)
-{
-    if (animating) {
-        float dt = deltaTimeInSeconds(currentTime);
-        if (dt <= 0.0f)
-            return animating;
-        const float minStepTime = 1.0f / 300.0f; // ~5 steps per frame
-        const int N = (dt > minStepTime) ? (1 + round(dt / minStepTime)) : 1;
-        dt /= N;
-        for (int i = 0; i < N; i++) {
-            // Force friction - always opposes motion
-            const float Ff = -frictionCoeff * velocity;
-
-            // Restoring force to match cards with slots
-            const float theta = startAngle + bias;
-            const float dtheta = 2.0f * M_PI / slotCount;
-            const float position = theta / dtheta;
-            const float fraction = position - floor(position); // fractional position between slots
-            float x;
-            if (fraction > 0.5f) {
-                x = - (1.0f - fraction);
-            } else {
-                x = fraction;
-            }
-            const float Fr = - springConstant * x;
-
-            // compute velocity
-            const float momentum = mass * velocity + (Ff + Fr)*dt;
-            velocity = momentum / mass;
-            bias += velocity * dt;
-        }
-
-        // TODO: Add animation to smoothly move back to slots. Currently snaps to location.
-        if (cardCount <= visibleSlotCount) {
-            // TODO: this aligns the cards to the first slot (theta = startAngle) when there aren't
-            // enough visible cards. It should be generalized to allow alignment to front,
-            // middle or back of the stack.
-            if (cardPosition(0) != slotPosition(0)) {
-                bias = 0.0f;
-            }
-        } else {
-            if (cardPosition(cardCount) < 0.0f) {
-                bias = -slotPosition(cardCount);
-            } else if (cardPosition(0) > slotPosition(0)) {
-                bias = 0.0f;
-            }
-        }
-
-        animating = fabs(velocity) > velocityThreshold;
-        if (!animating) {
-            const float dtheta = 2.0f * M_PI / slotCount;
-            bias = round((startAngle + bias) / dtheta) * dtheta - startAngle;
-            rsSendToClient(CMD_ANIMATION_FINISHED);
-        }
-    }
-    lastTime = currentTime;
-
-    return animating;
-}
-
-// Cull cards based on visibility and visibleSlotCount.
-// If visibleSlotCount is > 0, then only show those slots and cull the rest.
-// Otherwise, it should cull based on bounds of geometry.
-static int cullCards()
-{
-    const float thetaFirst = slotPosition(-1); // -1 keeps the card in front around a bit longer
-    const float thetaLast = slotPosition(visibleSlotCount);
-
-    int count = 0;
-    for (int i = 0; i < cardCount; i++) {
-        if (visibleSlotCount > 0) {
-            // If visibleSlotCount is specified, then only show up to visibleSlotCount cards.
-            float p = cardPosition(i);
-            if (p >= thetaFirst && p < thetaLast) {
-                cards[i].visible = true;
-                count++;
-            } else {
-                cards[i].visible = false;
-            }
-        } else {
-            // Cull the rest of the cards using bounding box of geometry.
-            // TODO
-            cards[i].visible = true;
-            count++;
-        }
-    }
-    return count;
-}
-
-// Request texture/geometry for items that have come into view
-// or doesn't have a texture yet.
-static void updateCardResources()
-{
-    for (int i = 0; i < cardCount; i++) {
-        int data[1];
-        if (cards[i].visible) {
-            // request texture from client if not loaded
-            if (cards[i].textureState == STATE_INVALID) {
-                data[0] = i;
-                bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data));
-                if (enqueued) {
-                    cards[i].textureState = STATE_LOADING;
-                } else {
-                    rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0);
-                }
-            }
-            // request geometry from client if not loaded
-            if (cards[i].geometryState == STATE_INVALID) {
-                data[0] = i;
-                bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data));
-                if (enqueued) {
-                    cards[i].geometryState = STATE_LOADING;
-                } else {
-                    rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", 0);
-                }
-            }
-        } else {
-            // ask the host to remove the texture
-            if (cards[i].textureState == STATE_LOADED) {
-                data[0] = i;
-                bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data));
-                if (enqueued) {
-                    cards[i].textureState = STATE_INVALID;
-                } else {
-                    rsDebug("Couldn't send CMD_INVALIDATE_TEXTURE", 0);
-                }
-            }
-            // ask the host to remove the geometry
-            if (cards[i].geometryState == STATE_LOADED) {
-                data[0] = i;
-                bool enqueued = rsSendToClient(CMD_INVALIDATE_GEOMETRY, data, sizeof(data));
-                if (enqueued) {
-                    cards[i].geometryState = STATE_INVALID;
-                } else {
-                    rsDebug("Couldn't send CMD_INVALIDATE_GEOMETRY", 0);
-                }
-            }
-
-        }
-    }
-}
-
-// Places dots on geometry to visually inspect that objects can be seen by rays.
-// NOTE: the color of the dot is somewhat random, as it depends on texture of previously-rendered
-// card.
-static void renderWithRays()
-{
-    const float w = rsgGetWidth();
-    const float h = rsgGetHeight();
-    const int skip = 8;
-    color(1.0f, 0.0f, 0.0f, 1.0f);
-    for (int j = 0; j < (int) h; j+=skip) {
-        float posY = (float) j;
-        for (int i = 0; i < (int) w; i+=skip) {
-            float posX = (float) i;
-            Ray ray;
-            if (makeRayForPixelAt(&ray, posX, posY)) {
-                float bestTime = FLT_MAX;
-                if (intersectGeometry(&ray, &bestTime) != -1) {
-                    rsgDrawSpriteScreenspace(posX, h - posY - 1, 0.0f, 2.0f, 2.0f);
-                }
-            }
-        }
-    }
-}
-
-int root() {
-    int64_t currentTime = rsUptimeMillis();
-
-    rsgClearDepth(1.0f);
-    rsgBindProgramVertex(vertexProgram);
-    rsgBindProgramFragment(fragmentProgram);
-    rsgBindProgramStore(programStore);
-    rsgBindProgramRaster(rasterProgram);
-
-    updateAllocationVars();
-
-    if (!initialized) {
-        for (int i = 0; i < cardCount; i++)
-            cards[i].textureState = STATE_INVALID;
-        initialized = true;
-    }
-
-    if (false) { // for debugging - flash the screen so we know we're still rendering
-        static bool toggle;
-        if (toggle)
-            rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 1.0);
-        else
-            rsgClearColor(1.0f, 0.0f, 0.0f, 1.f);
-        toggle = !toggle;
-    } else {
-        rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 1.0);
-    }
-
-    updateCameraMatrix(rsgGetWidth(), rsgGetHeight());
-
-    const bool timeExpired = (currentTime - touchTime) > ANIMATION_SCALE_TIME;
-    if (timeExpired) {
-        //currentSelection = -1;
-    }
-    bool stillAnimating = updateNextPosition(currentTime) || !timeExpired;
-
-    cullCards();
-
-    updateCardResources();
-
-    drawCards();
-
-    if (debugPicking) {
-        renderWithRays();
-    }
-
-    //rsSendToClient(CMD_PING);
-
-    return stillAnimating ? 1 : 0;
-}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index cb0bdd3..8310e56 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -96,7 +96,6 @@
 	android/graphics/MaskFilter.cpp \
 	android/graphics/Matrix.cpp \
 	android/graphics/Movie.cpp \
-	android/graphics/NIOBuffer.cpp \
 	android/graphics/NinePatch.cpp \
 	android/graphics/NinePatchImpl.cpp \
 	android/graphics/Paint.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6f6d919..c9d7e7f 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -308,6 +308,8 @@
     jclass clazz;
     jmethodID methodId;
 
+    LOGD("Calling main entry %s", className);
+
     env = getJNIEnv();
     if (env == NULL)
         return UNKNOWN_ERROR;
@@ -914,7 +916,8 @@
  */
 void AndroidRuntime::start(const char* className, const bool startSystemServer)
 {
-    LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n");
+    LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
+            className != NULL ? className : "(unknown)");
 
     char* slashClassName = NULL;
     char* cp;
@@ -1029,7 +1032,7 @@
 
 void AndroidRuntime::onExit(int code)
 {
-    LOGI("AndroidRuntime onExit calling exit(%d)", code);
+    LOGV("AndroidRuntime onExit calling exit(%d)", code);
     exit(code);
 }
 
@@ -1334,7 +1337,7 @@
      */
     androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
 
-    LOGD("--- registering native functions ---\n");
+    LOGV("--- registering native functions ---\n");
 
     /*
      * Every "register" function calls one or more things that return
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index b062264..95bb24f 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -12,6 +12,8 @@
 

 #include <jni.h>

 

+#include <Caches.h>

+

 #if 0

     #define TRACE_BITMAP(code)  code

 #else

@@ -251,6 +253,9 @@
 }

 

 static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {

+#ifdef USE_OPENGL_RENDERER

+    android::uirenderer::Caches::getInstance().textureCache.remove(bitmap);

+#endif

     delete bitmap;

 }

 

diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 578de6f..72cea65 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -2,7 +2,6 @@
 
 #include "jni.h"
 #include "GraphicsJNI.h"
-#include "NIOBuffer.h"
 #include "SkPicture.h"
 #include "SkRegion.h"
 #include <android_runtime/AndroidRuntime.h>
@@ -650,8 +649,6 @@
     gVMRuntime_trackExternalFreeMethodID =
                             env->GetMethodID(c, "trackExternalFree", "(J)V");
 
-    NIOBuffer::RegisterJNI(env);
-
     return 0;
 }
 
diff --git a/core/jni/android/graphics/NIOBuffer.cpp b/core/jni/android/graphics/NIOBuffer.cpp
deleted file mode 100644
index cb937a3..0000000
--- a/core/jni/android/graphics/NIOBuffer.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-#include "NIOBuffer.h"
-#include "GraphicsJNI.h"
-
-// enable this to dump each time we ref/unref a global java object (buffer)
-//
-//#define TRACE_GLOBAL_REFS
-
-//#define TRACE_ARRAY_LOCKS
-
-static jclass gNIOAccess_classID;
-static jmethodID gNIOAccess_getBasePointer;
-static jmethodID gNIOAccess_getBaseArray;
-static jmethodID gNIOAccess_getBaseArrayOffset;
-static jmethodID gNIOAccess_getRemainingBytes;
-
-void NIOBuffer::RegisterJNI(JNIEnv* env) {
-    if (0 != gNIOAccess_classID) {
-        return; // already called
-    }
-
-    jclass c = env->FindClass("java/nio/NIOAccess");
-    gNIOAccess_classID = (jclass)env->NewGlobalRef(c);
-
-    gNIOAccess_getBasePointer = env->GetStaticMethodID(gNIOAccess_classID,
-                                    "getBasePointer", "(Ljava/nio/Buffer;)J");
-    gNIOAccess_getBaseArray = env->GetStaticMethodID(gNIOAccess_classID,
-                    "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
-    gNIOAccess_getBaseArrayOffset = env->GetStaticMethodID(gNIOAccess_classID,
-                                "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
-    gNIOAccess_getRemainingBytes = env->GetStaticMethodID(gNIOAccess_classID,
-                                "getRemainingBytes", "(Ljava/nio/Buffer;)I");
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#ifdef TRACE_GLOBAL_REFS
-    static int gGlobalRefs;
-#endif
-
-#ifdef TRACE_ARRAY_LOCKS
-    static int gLockCount;
-#endif
-
-NIOBuffer::NIOBuffer(JNIEnv* env, jobject buffer) {
-    fBuffer = env->NewGlobalRef(buffer);
-#ifdef TRACE_GLOBAL_REFS
-    SkDebugf("------------ newglobalref bbuffer %X %d\n", buffer, gGlobalRefs++);
-#endif
-    fLockedPtr = NULL;
-    fLockedArray = NULL;
-}
-
-NIOBuffer::~NIOBuffer() {
-    // free() needs to have already been called
-    if (NULL != fBuffer) {
-        SkDebugf("----- leaked fBuffer in NIOBuffer");
-        sk_throw();
-    }
-}
-
-void NIOBuffer::free(JNIEnv* env) {
-
-    if (NULL != fLockedPtr) {
-        SkDebugf("======= free: array still locked %x %p\n", fLockedArray, fLockedPtr);
-    }
-    
-    
-    if (NULL != fBuffer) {
-#ifdef TRACE_GLOBAL_REFS
-        SkDebugf("----------- deleteglobalref buffer %X %d\n", fBuffer, --gGlobalRefs);
-#endif
-        env->DeleteGlobalRef(fBuffer);
-        fBuffer = NULL;
-    }
-}
-
-void* NIOBuffer::lock(JNIEnv* env, int* remaining) {
-    if (NULL != fLockedPtr) {
-        SkDebugf("======= lock: array still locked %x %p\n", fLockedArray, fLockedPtr);
-    }
-
-    fLockedPtr = NULL;
-    fLockedArray = NULL;
-
-    if (NULL != remaining) {
-        *remaining = env->CallStaticIntMethod(gNIOAccess_classID,
-                                              gNIOAccess_getRemainingBytes,
-                                              fBuffer);
-        if (GraphicsJNI::hasException(env)) {
-            return NULL;
-        }
-    }
-    
-    jlong pointer = env->CallStaticLongMethod(gNIOAccess_classID,
-                                              gNIOAccess_getBasePointer,
-                                              fBuffer);
-    if (GraphicsJNI::hasException(env)) {
-        return NULL;
-    }
-    if (0 != pointer) {
-        return reinterpret_cast<void*>(pointer);
-    }
-    
-    fLockedArray = (jbyteArray)env->CallStaticObjectMethod(gNIOAccess_classID,
-                                                        gNIOAccess_getBaseArray,
-                                                        fBuffer);
-    if (GraphicsJNI::hasException(env) || NULL == fLockedArray) {
-        return NULL;
-    }
-    jint offset = env->CallStaticIntMethod(gNIOAccess_classID,
-                                           gNIOAccess_getBaseArrayOffset,
-                                           fBuffer);
-    fLockedPtr = env->GetByteArrayElements(fLockedArray, NULL);
-    if (GraphicsJNI::hasException(env)) {
-        SkDebugf("------------ failed to lockarray %x\n", fLockedArray);
-        return NULL;
-    }
-#ifdef TRACE_ARRAY_LOCKS
-    SkDebugf("------------ lockarray %x %p %d\n",
-             fLockedArray, fLockedPtr, gLockCount++);
-#endif
-    if (NULL == fLockedPtr) {
-        offset = 0;
-    }
-    return (char*)fLockedPtr + offset;
-}
-
-void NIOBuffer::unlock(JNIEnv* env, bool dataChanged) {
-    if (NULL != fLockedPtr) {
-#ifdef TRACE_ARRAY_LOCKS
-        SkDebugf("------------ unlockarray %x %p %d\n",
-                 fLockedArray, fLockedPtr, --gLockCount);
-#endif
-        env->ReleaseByteArrayElements(fLockedArray, (jbyte*)fLockedPtr,
-                                      dataChanged ? 0 : JNI_ABORT);
-        
-        fLockedPtr = NULL;
-        fLockedArray = NULL;
-    } else {
-        SkDebugf("============= unlock called with null ptr %x\n", fLockedArray);
-    }
-}
-
diff --git a/core/jni/android/graphics/NIOBuffer.h b/core/jni/android/graphics/NIOBuffer.h
deleted file mode 100644
index 36b5554..0000000
--- a/core/jni/android/graphics/NIOBuffer.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef NIOBuffer_DEFINED
-#define NIOBuffer_DEFINED
-
-#include <jni.h>
-#include "SkBitmap.h"
-
-class NIOBuffer {
-public:
-    NIOBuffer(JNIEnv* env, jobject buffer);
-    // this checks to ensure that free() was called
-    ~NIOBuffer();
-
-    void* lock(JNIEnv* env, int* remaining);
-    void unlock(JNIEnv* env, bool dataChanged);
-    // must be called before destructor
-    void free(JNIEnv* env);
-
-    // call once on boot, to setup JNI globals
-    static void RegisterJNI(JNIEnv*);
-
-private:
-    jobject     fBuffer;
-    void*       fLockedPtr;
-    jbyteArray  fLockedArray;
-};
-
-#endif
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 0932473a..2517a8a 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -127,12 +127,13 @@
     close(mDispatchKeyWrite);
 }
 
-void AInputQueue::attachLooper(ALooper* looper, ALooper_callbackFunc* callback, void* data) {
+void AInputQueue::attachLooper(ALooper* looper, int ident,
+        ALooper_callbackFunc* callback, void* data) {
     mPollLoop = static_cast<android::PollLoop*>(looper);
     mPollLoop->setLooperCallback(mConsumer.getChannel()->getReceivePipeFd(),
-            POLLIN, callback, data);
+            ident, POLLIN, callback, data);
     mPollLoop->setLooperCallback(mDispatchKeyRead,
-            POLLIN, callback, data);
+            ident, POLLIN, callback, data);
 }
 
 void AInputQueue::detachLooper() {
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index 4b3f1c0..36e9089 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -593,6 +593,9 @@
         case SQLITE_MISMATCH:
            exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException";
            break;
+        case SQLITE_UNCLOSED:
+           exceptionClass = "android/database/sqlite/SQLiteUnfinalizedObjectsException";
+           break;
         default:
            exceptionClass = "android/database/sqlite/SQLiteException";
            break;
diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp
index 747ee50..e383123 100644
--- a/core/jni/android_database_SQLiteQuery.cpp
+++ b/core/jni/android_database_SQLiteQuery.cpp
@@ -205,7 +205,7 @@
                     int offset = window->alloc(size);
                     if (!offset) {
                         window->freeLastRow();
-                        LOGE("Failed allocating %u bytes for text/blob at %d,%d", size,
+                        LOGD("Failed allocating %u bytes for text/blob at %d,%d", size,
                                    startPos + numRows, i);
                         return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
                     }
@@ -225,7 +225,7 @@
                     int64_t value = sqlite3_column_int64(statement, i);
                     if (!window->putLong(numRows, i, value)) {
                         window->freeLastRow();
-                        LOGE("Failed allocating space for a long in column %d", i);
+                        LOGD("Failed allocating space for a long in column %d", i);
                         return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
                     }
                     LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + numRows, i, value);
@@ -234,7 +234,7 @@
                     double value = sqlite3_column_double(statement, i);
                     if (!window->putDouble(numRows, i, value)) {
                         window->freeLastRow();
-                        LOGE("Failed allocating space for a double in column %d", i);
+                        LOGD("Failed allocating space for a double in column %d", i);
                         return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
                     }
                     LOG_WINDOW("%d,%d is FLOAT %lf", startPos + numRows, i, value);
@@ -245,7 +245,7 @@
                     int offset = window->alloc(size);
                     if (!offset) {
                         window->freeLastRow();
-                        LOGE("Failed allocating %u bytes for blob at %d,%d", size,
+                        LOGD("Failed allocating %u bytes for blob at %d,%d", size,
                                    startPos + numRows, i);
                         return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
                     }
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index aec537d..c5ccc43 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -411,6 +411,30 @@
     return (jint)power;
 }
 
+static jboolean android_net_wifi_setBandCommand(JNIEnv* env, jobject clazz, jint band)
+{
+    char cmdstr[25];
+
+    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETBAND %d", band);
+    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
+
+    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+}
+
+static jint android_net_wifi_getBandCommand(JNIEnv* env, jobject clazz)
+{
+    char reply[25];
+    int band;
+
+    if (doCommand("DRIVER GETBAND", reply, sizeof(reply)) != 0) {
+        return (jint)-1;
+    }
+    // reply comes back in the form "Band X" where X is the
+    // number we're interested in.
+    sscanf(reply, "%*s %u", &band);
+    return (jint)band;
+}
+
 static jboolean android_net_wifi_setNumAllowedChannelsCommand(JNIEnv* env, jobject clazz, jint numChannels)
 {
     char cmdstr[256];
@@ -561,6 +585,8 @@
     { "stopPacketFiltering", "()Z", (void*) android_net_wifi_stopPacketFiltering },
     { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },
     { "getPowerModeCommand", "()I", (void*) android_net_wifi_getPowerModeCommand },
+    { "setBandCommand", "(I)Z", (void*) android_net_wifi_setBandCommand},
+    { "getBandCommand", "()I", (void*) android_net_wifi_getBandCommand},
     { "setNumAllowedChannelsCommand", "(I)Z", (void*) android_net_wifi_setNumAllowedChannelsCommand },
     { "getNumAllowedChannelsCommand", "()I", (void*) android_net_wifi_getNumAllowedChannelsCommand },
     { "setBluetoothCoexistenceModeCommand", "(I)Z",
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 6ef154e..1307ec3 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -50,6 +50,8 @@
 static jmethodID method_onDeviceCreated;
 static jmethodID method_onDeviceRemoved;
 static jmethodID method_onDeviceDisconnectRequested;
+static jmethodID method_onNetworkDeviceDisconnected;
+static jmethodID method_onNetworkDeviceConnected;
 
 static jmethodID method_onCreatePairedDeviceResult;
 static jmethodID method_onCreateDeviceResult;
@@ -100,6 +102,10 @@
     method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V");
     method_onDeviceDisconnectRequested = env->GetMethodID(clazz, "onDeviceDisconnectRequested",
                                                         "(Ljava/lang/String;)V");
+    method_onNetworkDeviceConnected = env->GetMethodID(clazz, "onNetworkDeviceConnected",
+                                                              "(Ljava/lang/String;I)V");
+    method_onNetworkDeviceDisconnected = env->GetMethodID(clazz, "onNetworkDeviceDisconnected",
+                                                              "(Ljava/lang/String;)V");
 
     method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
                                                          "(Ljava/lang/String;I)V");
@@ -253,6 +259,13 @@
             return JNI_FALSE;
         }
         dbus_bus_add_match(nat->conn,
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".NetworkServer'",
+                &err);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+            return JNI_FALSE;
+        }
+        dbus_bus_add_match(nat->conn,
                 "type='signal',interface='org.bluez.AudioSink'",
                 &err);
         if (dbus_error_is_set(&err)) {
@@ -409,13 +422,31 @@
         dbus_connection_unregister_object_path(nat->conn, agent_path);
 
         dbus_bus_remove_match(nat->conn,
-                "type='signal',interface='org.bluez.AudioSink'",
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".AudioSink'",
                 &err);
         if (dbus_error_is_set(&err)) {
             LOG_AND_FREE_DBUS_ERROR(&err);
         }
         dbus_bus_remove_match(nat->conn,
-                "type='signal',interface='org.bluez.audio.Device'",
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'",
+                &err);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+        }
+        dbus_bus_remove_match(nat->conn,
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Input'",
+                &err);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+        }
+        dbus_bus_remove_match(nat->conn,
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Network'",
+                &err);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+        }
+        dbus_bus_remove_match(nat->conn,
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".NetworkServer'",
                 &err);
         if (dbus_error_is_set(&err)) {
             LOG_AND_FREE_DBUS_ERROR(&err);
@@ -912,7 +943,39 @@
            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
        }
        goto success;
-   }
+    } else if (dbus_message_is_signal(msg,
+                                     "org.bluez.NetworkServer",
+                                     "DeviceDisconnected")) {
+       char *c_address;
+       if (dbus_message_get_args(msg, &err,
+                                  DBUS_TYPE_STRING, &c_address,
+                                  DBUS_TYPE_INVALID)) {
+           env->CallVoidMethod(nat->me,
+                               method_onNetworkDeviceDisconnected,
+                               env->NewStringUTF(c_address));
+       } else {
+           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+       }
+       goto success;
+    } else if (dbus_message_is_signal(msg,
+                                     "org.bluez.NetworkServer",
+                                     "DeviceConnected")) {
+       char *c_address;
+       uint16_t uuid;
+
+       if (dbus_message_get_args(msg, &err,
+                                  DBUS_TYPE_STRING, &c_address,
+                                  DBUS_TYPE_UINT16, &uuid,
+                                  DBUS_TYPE_INVALID)) {
+           env->CallVoidMethod(nat->me,
+                               method_onNetworkDeviceConnected,
+                               env->NewStringUTF(c_address),
+                               uuid);
+       } else {
+           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+       }
+       goto success;
+    }
 
     ret = a2dp_event_filter(msg, env);
     env->PopLocalFrame(NULL);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 7c99271..7dfb716 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -261,8 +261,7 @@
             continue;
         }
      
-        if (set_sched_policy(t_pid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
-                                            SP_BACKGROUND : SP_FOREGROUND)) {
+        if (androidSetThreadSchedulingGroup(t_pid, grp) != NO_ERROR) {
             signalExceptionForGroupError(env, clazz, errno);
             break;
         }
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index fe247e8..93fd54f 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -46,6 +46,7 @@
     jfieldID mYPrecision;
     jfieldID mEdgeFlags;
     jfieldID mMetaState;
+    jfieldID mFlags;
     jfieldID mNumPointers;
     jfieldID mNumSamples;
     jfieldID mPointerIdentifiers;
@@ -91,6 +92,8 @@
             event->getEdgeFlags());
     env->SetIntField(eventObj, gMotionEventClassInfo.mMetaState,
             event->getMetaState());
+    env->SetIntField(eventObj, gMotionEventClassInfo.mFlags,
+            event->getFlags());
     env->SetIntField(eventObj, gMotionEventClassInfo.mNumPointers,
             numPointers);
     env->SetIntField(eventObj, gMotionEventClassInfo.mNumSamples,
@@ -162,6 +165,7 @@
     jfloat yPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mYPrecision);
     jint edgeFlags = env->GetIntField(eventObj, gMotionEventClassInfo.mEdgeFlags);
     jint metaState = env->GetIntField(eventObj, gMotionEventClassInfo.mMetaState);
+    jint flags = env->GetIntField(eventObj, gMotionEventClassInfo.mFlags);
     jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers);
     jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples);
     jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj,
@@ -196,7 +200,7 @@
         samplePointerCoords[j].orientation = *(srcDataSamples++);
     }
 
-    event->initialize(deviceId, source, action, edgeFlags, metaState,
+    event->initialize(deviceId, source, action, flags, edgeFlags, metaState,
             xOffset, yOffset, xPrecision, yPrecision, downTimeNano, sampleEventTime,
             numPointers, pointerIdentifiers, samplePointerCoords);
 
@@ -281,6 +285,8 @@
             "mEdgeFlags", "I");
     GET_FIELD_ID(gMotionEventClassInfo.mMetaState, gMotionEventClassInfo.clazz,
             "mMetaState", "I");
+    GET_FIELD_ID(gMotionEventClassInfo.mFlags, gMotionEventClassInfo.clazz,
+            "mFlags", "I");
     GET_FIELD_ID(gMotionEventClassInfo.mNumPointers, gMotionEventClassInfo.clazz,
             "mNumPointers", "I");
     GET_FIELD_ID(gMotionEventClassInfo.mNumSamples, gMotionEventClassInfo.clazz,
diff --git a/core/res/Android.mk b/core/res/Android.mk
index 9fafc59..22f4fdc 100644
--- a/core/res/Android.mk
+++ b/core/res/Android.mk
@@ -36,12 +36,12 @@
 # Include resources generated by system RenderScript files.
 framework_GENERATED_SOURCE_DIR := $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)/src
 framework_RenderScript_STAMP_FILE := $(framework_GENERATED_SOURCE_DIR)/RenderScript.stamp
-LOCAL_RESOURCE_DIR := $(framework_GENERATED_SOURCE_DIR)/renderscript/res $(LOCAL_PATH)/res
+#LOCAL_RESOURCE_DIR := $(framework_GENERATED_SOURCE_DIR)/renderscript/res $(LOCAL_PATH)/res
 
 include $(BUILD_PACKAGE)
 
 # Make sure the system .rs files get compiled before building the package-export.apk.
-$(resource_export_package): $(framework_RenderScript_STAMP_FILE)
+# $(resource_export_package): $(framework_RenderScript_STAMP_FILE)
 
 # define a global intermediate target that other module may depend on.
 .PHONY: framework-res-package-target
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1f66d05..89298ea 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -50,6 +50,8 @@
     <protected-broadcast android:name="android.intent.action.ACTION_SHUTDOWN" />
     <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_LOW" />
     <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_OK" />
+    <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_FULL" />
+    <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_NOT_FULL" />
     <protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
     <protected-broadcast android:name="android.intent.action.REBOOT" />
     <protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
@@ -193,20 +195,6 @@
         android:label="@string/permlab_writeContacts"
         android:description="@string/permdesc_writeContacts" />
 
-    <!-- Allows an application to read the owner's data. -->
-    <permission android:name="android.permission.READ_OWNER_DATA"
-        android:permissionGroup="android.permission-group.PERSONAL_INFO"
-        android:protectionLevel="dangerous"
-        android:label="@string/permlab_readOwnerData"
-        android:description="@string/permdesc_readOwnerData" />
-
-    <!-- Allows an application to write (but not read) the owner's data. -->
-    <permission android:name="android.permission.WRITE_OWNER_DATA"
-        android:permissionGroup="android.permission-group.PERSONAL_INFO"
-        android:protectionLevel="dangerous"
-        android:label="@string/permlab_writeOwnerData"
-        android:description="@string/permdesc_writeOwnerData" />
-
     <!-- Allows an application to read the user's calendar data. -->
     <permission android:name="android.permission.READ_CALENDAR"
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
@@ -634,7 +622,10 @@
         android:label="@string/permlab_setAnimationScale"
         android:description="@string/permdesc_setAnimationScale" />
 
-    <!-- Allow an application to make its activities persistent. -->
+    <!-- @deprecated This functionality will be removed in the future; please do
+         not use.
+
+         Allow an application to make its activities persistent. -->
     <permission android:name="android.permission.PERSISTENT_ACTIVITY"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="dangerous"
@@ -776,7 +767,7 @@
     <!-- Allows applications to disable the keyguard -->
     <permission android:name="android.permission.DISABLE_KEYGUARD"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="normal"
+        android:protectionLevel="dangerous"
         android:description="@string/permdesc_disableKeyguard"
         android:label="@string/permlab_disableKeyguard" />
 
@@ -1285,7 +1276,7 @@
                 android:excludeFromRecents="true">
         </activity>
         <activity android:name="com.android.internal.app.PlatLogoActivity"
-                android:theme="@style/Theme.NoTitleBar.Fullscreen">
+                android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen">
         </activity>
         <activity android:name="com.android.internal.app.DisableCarModeActivity"
                 android:theme="@style/Theme.NoDisplay"
@@ -1322,6 +1313,10 @@
                 <action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.REBOOT" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
         </activity>
         <activity android:name="com.android.internal.app.NetInitiatedActivity"
                 android:theme="@style/Theme.Dialog.Alert"
diff --git a/core/res/res/anim/animator_fade_in.xml b/core/res/res/anim/animator_fade_in.xml
index cd5042f..2a28b4d 100644
--- a/core/res/res/anim/animator_fade_in.xml
+++ b/core/res/res/anim/animator_fade_in.xml
@@ -17,7 +17,7 @@
 */
 -->
 
-<property xmlns:android="http://schemas.android.com/apk/res/android"
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@anim/accelerate_interpolator"
     android:valueFrom="0"
     android:valueTo="1"
diff --git a/core/res/res/anim/animator_fade_out.xml b/core/res/res/anim/animator_fade_out.xml
index dfb5d9c..4db6591 100644
--- a/core/res/res/anim/animator_fade_out.xml
+++ b/core/res/res/anim/animator_fade_out.xml
@@ -17,7 +17,7 @@
 */
 -->
 
-<property xmlns:android="http://schemas.android.com/apk/res/android"
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@anim/accelerate_interpolator"
     android:valueFrom="1.0"
     android:valueTo="0.0"
diff --git a/core/res/res/anim/fragment_close_enter.xml b/core/res/res/anim/fragment_close_enter.xml
index d4091e8..53afa2a 100644
--- a/core/res/res/anim/fragment_close_enter.xml
+++ b/core/res/res/anim/fragment_close_enter.xml
@@ -16,33 +16,33 @@
 ** limitations under the License.
 */
 -->
-<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
-    <property
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
         android:interpolator="@anim/decelerate_interpolator"
         android:valueFrom="2"
         android:valueTo="1"
         android:valueType="floatType"
         android:propertyName="scaleX"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:interpolator="@anim/decelerate_interpolator"
         android:valueFrom="2"
         android:valueTo="1"
         android:valueType="floatType"
         android:propertyName="scaleY"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:interpolator="@anim/decelerate_interpolator"
         android:valueFrom="0"
         android:valueTo="1"
         android:valueType="floatType"
         android:propertyName="alpha"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:interpolator="@anim/decelerate_interpolator"
         android:valueFrom="-400"
         android:valueTo="0"
         android:valueType="floatType"
         android:propertyName="translationX"
         android:duration="@android:integer/config_mediumAnimTime"/>
-</sequencer>
\ No newline at end of file
+</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 3e2cd22..1554a4e 100644
--- a/core/res/res/anim/fragment_close_exit.xml
+++ b/core/res/res/anim/fragment_close_exit.xml
@@ -16,33 +16,33 @@
 ** limitations under the License.
 */
 -->
-<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
-    <property
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
         android:interpolator="@anim/accelerate_interpolator"
         android:valueFrom="1"
         android:valueTo=".5"
         android:valueType="floatType"
         android:propertyName="scaleX"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:interpolator="@anim/accelerate_interpolator"
         android:valueFrom="1"
         android:valueTo=".5"
         android:valueType="floatType"
         android:propertyName="scaleY"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:interpolator="@anim/accelerate_interpolator"
         android:valueFrom="1"
         android:valueTo="0"
         android:valueType="floatType"
         android:propertyName="alpha"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:interpolator="@anim/accelerate_interpolator"
         android:valueFrom="0"
         android:valueTo="400"
         android:valueType="floatType"
         android:propertyName="translationX"
         android:duration="@android:integer/config_mediumAnimTime"/>
-</sequencer>
\ No newline at end of file
+</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 c89001c..142f60c 100644
--- a/core/res/res/anim/fragment_open_enter.xml
+++ b/core/res/res/anim/fragment_open_enter.xml
@@ -16,31 +16,31 @@
 ** limitations under the License.
 */
 -->
-<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
-    <property
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
         android:interpolator="@anim/decelerate_interpolator"
         android:valueFrom="2"
         android:valueTo="1"
         android:valueType="floatType"
         android:propertyName="scaleX"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:interpolator="@anim/decelerate_interpolator"
         android:valueFrom="2"
         android:valueTo="1"
         android:valueType="floatType"
         android:propertyName="scaleY"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:valueFrom="0"
         android:valueTo="1"
         android:valueType="floatType"
         android:propertyName="alpha"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:valueFrom="400"
         android:valueTo="0"
         android:valueType="floatType"
         android:propertyName="translationX"
         android:duration="@android:integer/config_mediumAnimTime"/>
-</sequencer>
\ No newline at end of file
+</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 427fe4f..21260b9 100644
--- a/core/res/res/anim/fragment_open_exit.xml
+++ b/core/res/res/anim/fragment_open_exit.xml
@@ -16,31 +16,31 @@
 ** limitations under the License.
 */
 -->
-<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
-    <property
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
         android:interpolator="@anim/accelerate_interpolator"
         android:valueFrom="1"
         android:valueTo="2"
         android:valueType="floatType"
         android:propertyName="scaleX"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:interpolator="@anim/accelerate_interpolator"
         android:valueFrom="1"
         android:valueTo="2"
         android:valueType="floatType"
         android:propertyName="scaleY"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:valueFrom="1"
         android:valueTo="0"
         android:valueType="floatType"
         android:propertyName="alpha"
         android:duration="@android:integer/config_mediumAnimTime"/>
-    <property
+    <objectAnimator
         android:valueFrom="0"
         android:valueTo="-400"
         android:valueType="floatType"
         android:propertyName="translationX"
         android:duration="@android:integer/config_mediumAnimTime"/>
-</sequencer>
\ No newline at end of file
+</set>
\ No newline at end of file
diff --git a/core/res/res/drawable-nodpi/platlogo.jpg b/core/res/res/drawable-nodpi/platlogo.jpg
deleted file mode 100644
index 0e7780c..0000000
--- a/core/res/res/drawable-nodpi/platlogo.jpg
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.png b/core/res/res/drawable-nodpi/platlogo.png
new file mode 100644
index 0000000..e5af356
--- /dev/null
+++ b/core/res/res/drawable-nodpi/platlogo.png
Binary files differ
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index 516fb5f..115cb30 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -18,12 +18,12 @@
 */
 -->
 <resources>
-    <dimen name="status_bar_height">50dip</dimen>
+    <dimen name="status_bar_height">48dip</dimen>
     <!-- Height of the status bar -->
-    <dimen name="status_bar_icon_size">50dip</dimen>
+    <dimen name="status_bar_icon_size">48dip</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">50dip</dimen>
+    <dimen name="screen_margin_bottom">48dip</dimen>
     
     <!-- Default height of a key in the password keyboard for alpha -->
     <dimen name="password_keyboard_key_height_alpha">0.35in</dimen>
diff --git a/core/res/res/values-xlarge/styles.xml b/core/res/res/values-xlarge/styles.xml
index ff7df7c..4692656 100644
--- a/core/res/res/values-xlarge/styles.xml
+++ b/core/res/res/values-xlarge/styles.xml
@@ -27,7 +27,6 @@
     </style>
 
     <style name="TextAppearance.StatusBar.Icon">
-        <item name="android:textStyle">bold</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index be27528..b476b44 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1345,6 +1345,12 @@
              be saved. -->
         <attr name="saveEnabled" format="boolean" />
 
+        <!-- Specifies whether to filter touches when the view's window is obscured by
+             another visible window.  When set to true, the view will not receive touches
+             whenever a toast, dialog or other window appears above the view's window.
+             Refer to the {@link android.view.View} security documentation for more details. -->
+        <attr name="filterTouchesWhenObscured" format="boolean" />
+
         <!-- Defines the quality of translucent drawing caches. This property is used
              only when the drawing cache is enabled and translucent. The default value is auto. -->
         <attr name="drawingCacheQuality">
@@ -1403,6 +1409,11 @@
          of its subclasses.  Also see {@link #ViewGroup_Layout} for
          attributes that this class processes in its children. -->
     <declare-styleable name="ViewGroup">
+        <!-- Defines whether changes in layout (caused by adding and removing items) should
+             cause a LayoutTransition to run. When this flag is set to true, a default
+             LayoutTransition object will be set on the ViewGroup container and default
+             animations will run when these layout changes occur.-->
+        <attr name="animateLayoutChanges" format="boolean" />
         <!-- Defines whether a child is limited to draw inside of its bounds or not.
              This is useful with animations that scale the size of the children to more
              than 100% for instance. In such a case, this property should be set to false
@@ -3005,7 +3016,7 @@
     </declare-styleable>
 
     <!-- ========================== -->
-    <!-- Animator class attributes -->
+    <!-- ValueAnimator class attributes -->
     <!-- ========================== -->
     <eat-comment />
 
@@ -3042,7 +3053,7 @@
     </declare-styleable>
 
     <!-- ========================== -->
-    <!-- PropertyAnimator class attributes -->
+    <!-- ObjectAnimator class attributes -->
     <!-- ========================== -->
     <eat-comment />
 
@@ -3053,11 +3064,11 @@
 
 
     <!-- ========================== -->
-    <!-- Sequencer class attributes -->
+    <!-- AnimatorSet class attributes -->
     <!-- ========================== -->
     <eat-comment />
 
-    <declare-styleable name="Sequencer">
+    <declare-styleable name="AnimatorSet">
         <!-- Name of the property being animated. -->
         <attr name="ordering">
             <!-- child animations should be played together. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e439593..7bb56bd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1251,6 +1251,8 @@
   <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="filterTouchesWhenObscured" id="0x010102c4" />
+
   <public-padding type="attr" name="kraken_resource_pad" end="0x01010300" />
 
   <public-padding type="id" name="kraken_resource_pad" end="0x01020040" />
@@ -1265,7 +1267,13 @@
   <public type="drawable" name="presence_audio_online" id="0x010800b1" />
   <public-padding type="drawable" name="kraken_resource_pad" end="0x01080100" />
 
+  <public type="style" name="TextAppearance.StatusBar.Title" id="0x01030065" />
+  <public type="style" name="TextAppearance.StatusBar.Icon" id="0x01030066" />
+  <public type="style" name="TextAppearance.StatusBar.EventContent" id="0x01030067" />
+  <public type="style" name="TextAppearance.StatusBar.EventContent.Title" id="0x01030068" />
+
   <public-padding type="style" name="kraken_resource_pad" end="0x01030090" />
+
   <public-padding type="string" name="kraken_resource_pad" end="0x01040020" />
   <public-padding type="integer" name="kraken_resource_pad" end="0x010e0010" />
   <public-padding type="layout" name="kraken_resource_pad" end="0x01090020" />
@@ -1324,12 +1332,13 @@
   <public type="attr" name="imeSubtypeMode" />
   <public type="attr" name="imeSubtypeExtraValue" />
   <public type="attr" name="splitMotionEvents" />
+  <public type="attr" name="listChoiceBackgroundIndicator" />
+  <public type="attr" name="spinnerMode" />
+  <public type="attr" name="animateLayoutChanges" />
 
   <public type="anim" name="animator_fade_in" />
   <public type="anim" name="animator_fade_out" />
 
-  <public type="attr" name="listChoiceBackgroundIndicator" id="0x01010330" />
-
   <public type="id" name="home" />
   <!-- Context menu ID for the "Select text..." menu item to switch to text
        selection context mode in text views. -->
@@ -1359,4 +1368,5 @@
   <public type="layout" name="simple_selectable_list_item" id="0x01090022" />
 
   <public type="string" name="selectTextMode" id="0x01040030" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e9295f5..1981ac0 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -283,9 +283,15 @@
     <!-- Shutdown Confirmation Dialog.  When the user chooses to power off the phone, there will be a confirmation dialog.  This is the message. -->
     <string name="shutdown_confirm">Your phone will shut down.</string>
 
-    <!-- Recent Tasks dialog: title -->
+    <!-- Recent Tasks dialog: title
+     TODO: this should move to SystemUI.apk, but the code for the old 
+            recent dialog is still in the framework
+     -->
     <string name="recent_tasks_title">Recent</string>
-    <!-- Recent Tasks dialog: message when there are no recent applications -->
+    <!-- Recent Tasks dialog: message when there are no recent applications
+     TODO: this should move to SystemUI.apk, but the code for the old 
+            recent dialog is still in the framework
+     -->
     <string name="no_recent_tasks">No recent applications.</string>
 
     <!-- Title of the Global Actions Dialog -->
@@ -784,20 +790,6 @@
         applications can use this to erase or modify your contact data.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_writeOwnerData">write owner data</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeOwnerData">Allows an application to modify the
-        phone owner data stored on your phone. Malicious
-        applications can use this to erase or modify owner data.</string>
-
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_readOwnerData">read owner data</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readOwnerData">Allows an application read the
-        phone owner data stored on your phone. Malicious
-        applications can use this to read phone owner data.</string>
-
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readCalendar">read calendar events</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readCalendar">Allows an application to read all
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java
new file mode 100644
index 0000000..33e9dd7
--- /dev/null
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.bluetooth;
+
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+/**
+ * Instrumentation test case for stress test involving rebooting the device.
+ * <p>
+ * This test case tests that bluetooth is enabled after a device reboot. Because
+ * the device will reboot, the instrumentation must be driven by a script on the
+ * host side.
+ */
+public class BluetoothRebootStressTest extends InstrumentationTestCase {
+    private static final String TAG = "BluetoothRebootStressTest";
+    private static final String OUTPUT_FILE = "BluetoothRebootStressTestOutput.txt";
+
+    private BluetoothTestUtils mTestUtils;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        Context context = getInstrumentation().getTargetContext();
+        mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        mTestUtils.close();
+    }
+
+    /**
+     * Test method used to start the test by turning bluetooth on.
+     */
+    public void testStart() {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        mTestUtils.enable(adapter);
+    }
+
+    /**
+     * Test method used in the middle iterations of the test to check if
+     * bluetooth is on. Does not toggle bluetooth after the check. Assumes that
+     * bluetooth has been turned on by {@code #testStart()}
+     */
+    public void testMiddleNoToggle() {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+        assertTrue(adapter.isEnabled());
+    }
+
+    /**
+     * Test method used in the middle iterations of the test to check if
+     * bluetooth is on. Toggles bluetooth after the check. Assumes that
+     * bluetooth has been turned on by {@code #testStart()}
+     */
+    public void testMiddleToggle() {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+        assertTrue(adapter.isEnabled());
+
+        mTestUtils.disable(adapter);
+        mTestUtils.enable(adapter);
+    }
+
+    /**
+     * Test method used in the stop the test by turning bluetooth off. Assumes
+     * that bluetooth has been turned on by {@code #testStart()}
+     */
+    public void testStop() {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+        assertTrue(adapter.isEnabled());
+
+        mTestUtils.disable(adapter);
+    }
+}
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
index 7a6ee8e..d8d9eba 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -16,168 +16,28 @@
 
 package android.bluetooth;
 
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-
-import android.app.Instrumentation;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Environment;
 import android.test.InstrumentationTestCase;
-import android.util.Log;
 
 public class BluetoothStressTest extends InstrumentationTestCase {
     private static final String TAG = "BluetoothStressTest";
     private static final String OUTPUT_FILE = "BluetoothStressTestOutput.txt";
 
-    /**
-     * Timeout for {@link BluetoothAdapter#disable()} in ms.
-     */
-    private static final int DISABLE_TIMEOUT = 5000;
-
-    /**
-     * Timeout for {@link BluetoothAdapter#enable()} in ms.
-     */
-    private static final int ENABLE_TIMEOUT = 20000;
-
-    /**
-     * Timeout for {@link BluetoothAdapter#setScanMode(int)} in ms.
-     */
-    private static final int SET_SCAN_MODE_TIMEOUT = 5000;
-
-    /**
-     * Timeout for {@link BluetoothAdapter#startDiscovery()} in ms.
-     */
-    private static final int START_DISCOVERY_TIMEOUT = 5000;
-
-    /**
-     * Timeout for {@link BluetoothAdapter#cancelDiscovery()} in ms.
-     */
-    private static final int CANCEL_DISCOVERY_TIMEOUT = 5000;
-
-    private static final int DISCOVERY_STARTED_FLAG = 1;
-    private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
-    private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
-    private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
-    private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
-    private static final int STATE_OFF_FLAG = 1 << 5;
-    private static final int STATE_TURNING_ON_FLAG = 1 << 6;
-    private static final int STATE_ON_FLAG = 1 << 7;
-    private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
-
-    /**
-     * Time between polls in ms.
-     */
-    private static final int POLL_TIME = 100;
-
-    private Context mContext;
-
-    private Instrumentation mInstrumentation;
-
-    private BufferedWriter mOutputWriter;
-
-    private class BluetoothReceiver extends BroadcastReceiver {
-        private int mFiredFlags = 0;
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            synchronized (this) {
-                if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
-                    mFiredFlags |= DISCOVERY_STARTED_FLAG;
-                } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
-                    mFiredFlags |= DISCOVERY_FINISHED_FLAG;
-                } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
-                    int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
-                            BluetoothAdapter.ERROR);
-                    assertNotSame(mode, BluetoothAdapter.ERROR);
-                    switch (mode) {
-                        case BluetoothAdapter.SCAN_MODE_NONE:
-                            mFiredFlags |= SCAN_MODE_NONE_FLAG;
-                            break;
-                        case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
-                            mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG;
-                            break;
-                        case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
-                            mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
-                            break;
-                    }
-                } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
-                    int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-                            BluetoothAdapter.ERROR);
-                    assertNotSame(state, BluetoothAdapter.ERROR);
-                    switch (state) {
-                        case BluetoothAdapter.STATE_OFF:
-                            mFiredFlags |= STATE_OFF_FLAG;
-                            break;
-                        case BluetoothAdapter.STATE_TURNING_ON:
-                            mFiredFlags |= STATE_TURNING_ON_FLAG;
-                            break;
-                        case BluetoothAdapter.STATE_ON:
-                            mFiredFlags |= STATE_ON_FLAG;
-                            break;
-                        case BluetoothAdapter.STATE_TURNING_OFF:
-                            mFiredFlags |= STATE_TURNING_OFF_FLAG;
-                            break;
-                    }
-                }
-            }
-        }
-
-        public int getFiredFlags() {
-            synchronized (this) {
-                return mFiredFlags;
-            }
-        }
-
-        public void resetFiredFlags() {
-            synchronized (this) {
-                mFiredFlags = 0;
-            }
-        }
-    }
-
-    private BluetoothReceiver mReceiver = new BluetoothReceiver();
+    private BluetoothTestUtils mTestUtils;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
-        mInstrumentation = getInstrumentation();
-        mContext = mInstrumentation.getTargetContext();
-
-        try {
-            mOutputWriter = new BufferedWriter(new FileWriter(new File(
-                    Environment.getExternalStorageDirectory(), OUTPUT_FILE), true));
-        } catch (IOException e) {
-            Log.w(TAG, "Test output file could not be opened", e);
-            mOutputWriter = null;
-        }
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
-        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
-        filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
-        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-        mContext.registerReceiver(mReceiver, filter);
+        Context context = getInstrumentation().getTargetContext();
+        mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE);
     }
 
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
 
-        mContext.unregisterReceiver(mReceiver);
-
-        if (mOutputWriter != null) {
-            try {
-                mOutputWriter.close();
-            } catch (IOException e) {
-                Log.w(TAG, "Test output file could not be closed", e);
-            }
-        }
+        mTestUtils.close();
     }
 
     public void testEnable() {
@@ -185,295 +45,37 @@
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
 
         for (int i = 0; i < iterations; i++) {
-            writeOutput("enable iteration " + (i + 1) + " of " + iterations);
-            enable(adapter);
-            disable(adapter);
+            mTestUtils.writeOutput("enable iteration " + (i + 1) + " of " + iterations);
+            mTestUtils.enable(adapter);
+            mTestUtils.disable(adapter);
         }
     }
 
     public void testDiscoverable() {
         int iterations = BluetoothTestRunner.sDiscoverableIterations;
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        enable(adapter);
+        mTestUtils.enable(adapter);
 
         for (int i = 0; i < iterations; i++) {
-            writeOutput("discoverable iteration " + (i + 1) + " of " + iterations);
-            discoverable(adapter);
-            undiscoverable(adapter);
+            mTestUtils.writeOutput("discoverable iteration " + (i + 1) + " of " + iterations);
+            mTestUtils.discoverable(adapter);
+            mTestUtils.undiscoverable(adapter);
         }
 
-        disable(adapter);
+        mTestUtils.disable(adapter);
     }
 
     public void testScan() {
         int iterations = BluetoothTestRunner.sScanIterations;
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        enable(adapter);
+        mTestUtils.enable(adapter);
 
         for (int i = 0; i < iterations; i++) {
-            writeOutput("scan iteration " + (i + 1) + " of " + iterations);
-            startScan(adapter);
-            stopScan(adapter);
+            mTestUtils.writeOutput("scan iteration " + (i + 1) + " of " + iterations);
+            mTestUtils.startScan(adapter);
+            mTestUtils.stopScan(adapter);
         }
 
-        disable(adapter);
-    }
-
-    private void disable(BluetoothAdapter adapter) {
-        int mask = STATE_TURNING_OFF_FLAG | STATE_OFF_FLAG | SCAN_MODE_NONE_FLAG;
-        mReceiver.resetFiredFlags();
-
-        int state = adapter.getState();
-        switch (state) {
-            case BluetoothAdapter.STATE_OFF:
-                assertFalse(adapter.isEnabled());
-                return;
-            case BluetoothAdapter.STATE_ON:
-                assertTrue(adapter.isEnabled());
-                assertTrue(adapter.disable());
-                break;
-            case BluetoothAdapter.STATE_TURNING_ON:
-                assertFalse(adapter.isEnabled());
-                assertTrue(adapter.disable());
-                break;
-            case BluetoothAdapter.STATE_TURNING_OFF:
-                assertFalse(adapter.isEnabled());
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            default:
-                fail("disable() invalid state: state=" + state);
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < DISABLE_TIMEOUT) {
-            state = adapter.getState();
-            if (state == BluetoothAdapter.STATE_OFF) {
-                assertFalse(adapter.isEnabled());
-                if ((mReceiver.getFiredFlags() & mask) == mask) {
-                    mReceiver.resetFiredFlags();
-                    writeOutput(String.format("disable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
-                    return;
-                }
-            } else {
-                assertFalse(adapter.isEnabled());
-                assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state);
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
-        fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
-                state, BluetoothAdapter.STATE_OFF, firedFlags, mask));
-    }
-
-    private void enable(BluetoothAdapter adapter) {
-        int mask = STATE_TURNING_ON_FLAG | STATE_ON_FLAG | SCAN_MODE_CONNECTABLE_FLAG;
-        mReceiver.resetFiredFlags();
-
-        int state = adapter.getState();
-        switch (state) {
-            case BluetoothAdapter.STATE_ON:
-                assertTrue(adapter.isEnabled());
-                return;
-            case BluetoothAdapter.STATE_OFF:
-            case BluetoothAdapter.STATE_TURNING_OFF:
-                assertFalse(adapter.isEnabled());
-                assertTrue(adapter.enable());
-                break;
-            case BluetoothAdapter.STATE_TURNING_ON:
-                assertFalse(adapter.isEnabled());
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            default:
-                fail("enable() invalid state: state=" + state);
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < ENABLE_TIMEOUT) {
-            state = adapter.getState();
-            if (state == BluetoothAdapter.STATE_ON) {
-                assertTrue(adapter.isEnabled());
-                if ((mReceiver.getFiredFlags() & mask) == mask) {
-                    mReceiver.resetFiredFlags();
-                    writeOutput(String.format("enable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
-                    return;
-                }
-            } else {
-                assertFalse(adapter.isEnabled());
-                assertEquals(BluetoothAdapter.STATE_TURNING_ON, state);
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
-        fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
-                state, BluetoothAdapter.STATE_ON, firedFlags, mask));
-    }
-
-    private void discoverable(BluetoothAdapter adapter) {
-        int mask = SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
-        mReceiver.resetFiredFlags();
-
-        if (!adapter.isEnabled()) {
-            fail("discoverable() bluetooth not enabled");
-        }
-
-        int scanMode = adapter.getScanMode();
-        if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
-            return;
-        }
-
-        assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
-        assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
-            scanMode = adapter.getScanMode();
-            if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
-                if ((mReceiver.getFiredFlags() & mask) == mask) {
-                    mReceiver.resetFiredFlags();
-                    writeOutput(String.format("discoverable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
-                    return;
-                }
-            } else {
-                assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
-        fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
-                + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,
-                firedFlags, mask));
-    }
-
-    private void undiscoverable(BluetoothAdapter adapter) {
-        int mask = SCAN_MODE_CONNECTABLE_FLAG;
-        mReceiver.resetFiredFlags();
-
-        if (!adapter.isEnabled()) {
-            fail("undiscoverable() bluetooth not enabled");
-        }
-
-        int scanMode = adapter.getScanMode();
-        if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
-            return;
-        }
-
-        assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
-        assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
-            scanMode = adapter.getScanMode();
-            if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
-                if ((mReceiver.getFiredFlags() & mask) == mask) {
-                    mReceiver.resetFiredFlags();
-                    writeOutput(String.format("undiscoverable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
-                    return;
-                }
-            } else {
-                assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
-        fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
-                + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags,
-                mask));
-    }
-
-    private void startScan(BluetoothAdapter adapter) {
-        int mask = DISCOVERY_STARTED_FLAG;
-        mReceiver.resetFiredFlags();
-
-        if (!adapter.isEnabled()) {
-            fail("startScan() bluetooth not enabled");
-        }
-
-        if (adapter.isDiscovering()) {
-            return;
-        }
-
-        assertTrue(adapter.startDiscovery());
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) {
-            if (adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
-                mReceiver.resetFiredFlags();
-                writeOutput(String.format("startScan() completed in %d ms",
-                        (System.currentTimeMillis() - s)));
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
-        fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
-                adapter.isDiscovering(), firedFlags, mask));
-    }
-
-    private void stopScan(BluetoothAdapter adapter) {
-        int mask = DISCOVERY_FINISHED_FLAG;
-        mReceiver.resetFiredFlags();
-
-        if (!adapter.isEnabled()) {
-            fail("stopScan() bluetooth not enabled");
-        }
-
-        if (!adapter.isDiscovering()) {
-            return;
-        }
-
-        // TODO: put assertTrue() around cancelDiscovery() once it starts
-        // returning true.
-        adapter.cancelDiscovery();
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) {
-            if (!adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
-                mReceiver.resetFiredFlags();
-                writeOutput(String.format("stopScan() completed in %d ms",
-                        (System.currentTimeMillis() - s)));
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
-        fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
-                adapter.isDiscovering(), firedFlags, mask));
-
-    }
-
-    private void writeOutput(String s) {
-        if (mOutputWriter == null) {
-            return;
-        }
-        try {
-            Log.i(TAG, s);
-            mOutputWriter.write(s + "\n");
-            mOutputWriter.flush();
-        } catch (IOException e) {
-            Log.w(TAG, "Could not write to output file", e);
-        }
-    }
-
-    private void sleep(long time) {
-        try {
-            Thread.sleep(time);
-        } catch (InterruptedException e) {
-        }
+        mTestUtils.disable(adapter);
     }
 }
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
new file mode 100644
index 0000000..82de509
--- /dev/null
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
@@ -0,0 +1,441 @@
+/*
+ * 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.bluetooth;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Environment;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class BluetoothTestUtils extends Assert {
+
+    /**
+     * Timeout for {@link BluetoothAdapter#disable()} in ms.
+     */
+    private static final int DISABLE_TIMEOUT = 5000;
+
+    /**
+     * Timeout for {@link BluetoothAdapter#enable()} in ms.
+     */
+    private static final int ENABLE_TIMEOUT = 20000;
+
+    /**
+     * Timeout for {@link BluetoothAdapter#setScanMode(int)} in ms.
+     */
+    private static final int SET_SCAN_MODE_TIMEOUT = 5000;
+
+    /**
+     * Timeout for {@link BluetoothAdapter#startDiscovery()} in ms.
+     */
+    private static final int START_DISCOVERY_TIMEOUT = 5000;
+
+    /**
+     * Timeout for {@link BluetoothAdapter#cancelDiscovery()} in ms.
+     */
+    private static final int CANCEL_DISCOVERY_TIMEOUT = 5000;
+
+    private static final int DISCOVERY_STARTED_FLAG = 1;
+    private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
+    private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
+    private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
+    private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
+    private static final int STATE_OFF_FLAG = 1 << 5;
+    private static final int STATE_TURNING_ON_FLAG = 1 << 6;
+    private static final int STATE_ON_FLAG = 1 << 7;
+    private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
+
+    /**
+     * Time between polls in ms.
+     */
+    private static final int POLL_TIME = 100;
+
+    private Context mContext;
+
+    private BufferedWriter mOutputWriter;
+
+    private String mOutputFile;
+    private String mTag;
+
+    private class BluetoothReceiver extends BroadcastReceiver {
+        private int mFiredFlags = 0;
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (this) {
+                if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
+                    mFiredFlags |= DISCOVERY_STARTED_FLAG;
+                } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
+                    mFiredFlags |= DISCOVERY_FINISHED_FLAG;
+                } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
+                    int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
+                            BluetoothAdapter.ERROR);
+                    assertNotSame(mode, BluetoothAdapter.ERROR);
+                    switch (mode) {
+                        case BluetoothAdapter.SCAN_MODE_NONE:
+                            mFiredFlags |= SCAN_MODE_NONE_FLAG;
+                            break;
+                        case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
+                            mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG;
+                            break;
+                        case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
+                            mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
+                            break;
+                    }
+                } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+                    int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+                            BluetoothAdapter.ERROR);
+                    assertNotSame(state, BluetoothAdapter.ERROR);
+                    switch (state) {
+                        case BluetoothAdapter.STATE_OFF:
+                            mFiredFlags |= STATE_OFF_FLAG;
+                            break;
+                        case BluetoothAdapter.STATE_TURNING_ON:
+                            mFiredFlags |= STATE_TURNING_ON_FLAG;
+                            break;
+                        case BluetoothAdapter.STATE_ON:
+                            mFiredFlags |= STATE_ON_FLAG;
+                            break;
+                        case BluetoothAdapter.STATE_TURNING_OFF:
+                            mFiredFlags |= STATE_TURNING_OFF_FLAG;
+                            break;
+                    }
+                }
+            }
+        }
+
+        public int getFiredFlags() {
+            synchronized (this) {
+                return mFiredFlags;
+            }
+        }
+
+        public void resetFiredFlags() {
+            synchronized (this) {
+                mFiredFlags = 0;
+            }
+        }
+    }
+
+    private BluetoothReceiver mReceiver = new BluetoothReceiver();
+
+    public BluetoothTestUtils(Context context, String tag) {
+        this(context, tag, null);
+    }
+
+    public BluetoothTestUtils(Context context, String tag, String outputFile) {
+        mContext = context;
+        mTag = tag;
+        mOutputFile = outputFile;
+
+        if (mOutputFile == null) {
+            mOutputWriter = null;
+        } else {
+            try {
+                mOutputWriter = new BufferedWriter(new FileWriter(new File(
+                        Environment.getExternalStorageDirectory(), mOutputFile), true));
+            } catch (IOException e) {
+                Log.w(mTag, "Test output file could not be opened", e);
+                mOutputWriter = null;
+            }
+        }
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
+        filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    public void close() {
+        mContext.unregisterReceiver(mReceiver);
+
+        if (mOutputWriter != null) {
+            try {
+                mOutputWriter.close();
+            } catch (IOException e) {
+                Log.w(mTag, "Test output file could not be closed", e);
+            }
+        }
+    }
+
+    public void enable(BluetoothAdapter adapter) {
+        int mask = STATE_TURNING_ON_FLAG | STATE_ON_FLAG | SCAN_MODE_CONNECTABLE_FLAG;
+        mReceiver.resetFiredFlags();
+
+        int state = adapter.getState();
+        switch (state) {
+            case BluetoothAdapter.STATE_ON:
+                assertTrue(adapter.isEnabled());
+                return;
+            case BluetoothAdapter.STATE_OFF:
+            case BluetoothAdapter.STATE_TURNING_OFF:
+                assertFalse(adapter.isEnabled());
+                assertTrue(adapter.enable());
+                break;
+            case BluetoothAdapter.STATE_TURNING_ON:
+                assertFalse(adapter.isEnabled());
+                mask = 0; // Don't check for received intents since we might have missed them.
+                break;
+            default:
+                fail("enable() invalid state: state=" + state);
+        }
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < ENABLE_TIMEOUT) {
+            state = adapter.getState();
+            if (state == BluetoothAdapter.STATE_ON) {
+                assertTrue(adapter.isEnabled());
+                if ((mReceiver.getFiredFlags() & mask) == mask) {
+                    mReceiver.resetFiredFlags();
+                    writeOutput(String.format("enable() completed in %d ms",
+                            (System.currentTimeMillis() - s)));
+                    return;
+                }
+            } else {
+                assertFalse(adapter.isEnabled());
+                assertEquals(BluetoothAdapter.STATE_TURNING_ON, state);
+            }
+            sleep(POLL_TIME);
+        }
+
+        int firedFlags = mReceiver.getFiredFlags();
+        mReceiver.resetFiredFlags();
+        fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
+                state, BluetoothAdapter.STATE_ON, firedFlags, mask));
+    }
+
+    public void disable(BluetoothAdapter adapter) {
+        int mask = STATE_TURNING_OFF_FLAG | STATE_OFF_FLAG | SCAN_MODE_NONE_FLAG;
+        mReceiver.resetFiredFlags();
+
+        int state = adapter.getState();
+        switch (state) {
+            case BluetoothAdapter.STATE_OFF:
+                assertFalse(adapter.isEnabled());
+                return;
+            case BluetoothAdapter.STATE_ON:
+                assertTrue(adapter.isEnabled());
+                assertTrue(adapter.disable());
+                break;
+            case BluetoothAdapter.STATE_TURNING_ON:
+                assertFalse(adapter.isEnabled());
+                assertTrue(adapter.disable());
+                break;
+            case BluetoothAdapter.STATE_TURNING_OFF:
+                assertFalse(adapter.isEnabled());
+                mask = 0; // Don't check for received intents since we might have missed them.
+                break;
+            default:
+                fail("disable() invalid state: state=" + state);
+        }
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < DISABLE_TIMEOUT) {
+            state = adapter.getState();
+            if (state == BluetoothAdapter.STATE_OFF) {
+                assertFalse(adapter.isEnabled());
+                if ((mReceiver.getFiredFlags() & mask) == mask) {
+                    mReceiver.resetFiredFlags();
+                    writeOutput(String.format("disable() completed in %d ms",
+                            (System.currentTimeMillis() - s)));
+                    return;
+                }
+            } else {
+                assertFalse(adapter.isEnabled());
+                assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state);
+            }
+            sleep(POLL_TIME);
+        }
+
+        int firedFlags = mReceiver.getFiredFlags();
+        mReceiver.resetFiredFlags();
+        fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
+                state, BluetoothAdapter.STATE_OFF, firedFlags, mask));
+    }
+
+    public void discoverable(BluetoothAdapter adapter) {
+        int mask = SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
+        mReceiver.resetFiredFlags();
+
+        if (!adapter.isEnabled()) {
+            fail("discoverable() bluetooth not enabled");
+        }
+
+        int scanMode = adapter.getScanMode();
+        if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+            return;
+        }
+
+        assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+        assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+            scanMode = adapter.getScanMode();
+            if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+                if ((mReceiver.getFiredFlags() & mask) == mask) {
+                    mReceiver.resetFiredFlags();
+                    writeOutput(String.format("discoverable() completed in %d ms",
+                            (System.currentTimeMillis() - s)));
+                    return;
+                }
+            } else {
+                assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+            }
+            sleep(POLL_TIME);
+        }
+
+        int firedFlags = mReceiver.getFiredFlags();
+        mReceiver.resetFiredFlags();
+        fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
+                + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,
+                firedFlags, mask));
+    }
+
+    public void undiscoverable(BluetoothAdapter adapter) {
+        int mask = SCAN_MODE_CONNECTABLE_FLAG;
+        mReceiver.resetFiredFlags();
+
+        if (!adapter.isEnabled()) {
+            fail("undiscoverable() bluetooth not enabled");
+        }
+
+        int scanMode = adapter.getScanMode();
+        if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+            return;
+        }
+
+        assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+        assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+            scanMode = adapter.getScanMode();
+            if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+                if ((mReceiver.getFiredFlags() & mask) == mask) {
+                    mReceiver.resetFiredFlags();
+                    writeOutput(String.format("undiscoverable() completed in %d ms",
+                            (System.currentTimeMillis() - s)));
+                    return;
+                }
+            } else {
+                assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+            }
+            sleep(POLL_TIME);
+        }
+
+        int firedFlags = mReceiver.getFiredFlags();
+        mReceiver.resetFiredFlags();
+        fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
+                + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags,
+                mask));
+    }
+
+    public void startScan(BluetoothAdapter adapter) {
+        int mask = DISCOVERY_STARTED_FLAG;
+        mReceiver.resetFiredFlags();
+
+        if (!adapter.isEnabled()) {
+            fail("startScan() bluetooth not enabled");
+        }
+
+        if (adapter.isDiscovering()) {
+            return;
+        }
+
+        assertTrue(adapter.startDiscovery());
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) {
+            if (adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
+                mReceiver.resetFiredFlags();
+                writeOutput(String.format("startScan() completed in %d ms",
+                        (System.currentTimeMillis() - s)));
+                return;
+            }
+            sleep(POLL_TIME);
+        }
+
+        int firedFlags = mReceiver.getFiredFlags();
+        mReceiver.resetFiredFlags();
+        fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
+                adapter.isDiscovering(), firedFlags, mask));
+    }
+
+    public void stopScan(BluetoothAdapter adapter) {
+        int mask = DISCOVERY_FINISHED_FLAG;
+        mReceiver.resetFiredFlags();
+
+        if (!adapter.isEnabled()) {
+            fail("stopScan() bluetooth not enabled");
+        }
+
+        if (!adapter.isDiscovering()) {
+            return;
+        }
+
+        // TODO: put assertTrue() around cancelDiscovery() once it starts returning true.
+        adapter.cancelDiscovery();
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) {
+            if (!adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
+                mReceiver.resetFiredFlags();
+                writeOutput(String.format("stopScan() completed in %d ms",
+                        (System.currentTimeMillis() - s)));
+                return;
+            }
+            sleep(POLL_TIME);
+        }
+
+        int firedFlags = mReceiver.getFiredFlags();
+        mReceiver.resetFiredFlags();
+        fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
+                adapter.isDiscovering(), firedFlags, mask));
+
+    }
+
+    public void writeOutput(String s) {
+        Log.i(mTag, s);
+        if (mOutputWriter == null) {
+            return;
+        }
+        try {
+            mOutputWriter.write(s + "\n");
+            mOutputWriter.flush();
+        } catch (IOException e) {
+            Log.w(mTag, "Could not write to output file", e);
+        }
+    }
+
+    private void sleep(long time) {
+        try {
+            Thread.sleep(time);
+        } catch (InterruptedException e) {
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseErrorHandlerTest.java b/core/tests/coretests/src/android/database/DatabaseErrorHandlerTest.java
new file mode 100644
index 0000000..48d25b9
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseErrorHandlerTest.java
@@ -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.
+ */
+
+package android.database;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class DatabaseErrorHandlerTest extends AndroidTestCase {
+
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private static final String DB_NAME = "database_test.db";
+    private File dbDir;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
+        mDatabaseFile = new File(dbDir, DB_NAME);
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null,
+                new MyDatabaseCorruptionHandler());
+        assertNotNull(mDatabase);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    public void testNoCorruptionCase() {
+        new MyDatabaseCorruptionHandler().onCorruption(mDatabase);
+        // database file should still exist
+        assertTrue(mDatabaseFile.exists());
+    }
+
+    public void testDatabaseIsCorrupt() throws IOException {
+        mDatabase.execSQL("create table t (i int);");
+        // write junk into the database file
+        BufferedWriter writer = new BufferedWriter(new FileWriter(mDatabaseFile.getPath()));
+        writer.write("blah");
+        writer.close();
+        assertTrue(mDatabaseFile.exists());
+        // since the database file is now corrupt, doing any sql on this database connection
+        // should trigger call to MyDatabaseCorruptionHandler.onCorruption
+        try {
+            mDatabase.execSQL("select * from t;");
+            fail("expected exception");
+        } catch (SQLiteException e) {
+            // expected
+        }
+        // after corruption handler is called, the database file should be free of
+        // database corruption
+        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null,
+                new MyDatabaseCorruptionHandler());
+        assertTrue(db.isDatabaseIntegrityOk());
+    }
+
+    /**
+     * An example implementation of {@link DatabaseErrorHandler} to demonstrate
+     * database corruption handler which checks to make sure database is indeed
+     * corrupt before deleting the file.
+     */
+    public class MyDatabaseCorruptionHandler implements DatabaseErrorHandler {
+        public void onCorruption(SQLiteDatabase dbObj) {
+            boolean databaseOk = dbObj.isDatabaseIntegrityOk();
+            // close the database
+            try {
+                dbObj.close();
+            } catch (SQLiteException e) {
+                /* ignore */
+            }
+            if (databaseOk) {
+                // database is just fine. no need to delete the database file
+                Log.e("MyDatabaseCorruptionHandler", "no corruption in the database: " +
+                        mDatabaseFile.getPath());
+            } else {
+                // database is corrupt. delete the database file
+                Log.e("MyDatabaseCorruptionHandler", "deleting the database file: " +
+                        mDatabaseFile.getPath());
+                new File(dbDir, DB_NAME).delete();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteUnfinalizedExceptionTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteUnfinalizedExceptionTest.java
new file mode 100644
index 0000000..cd2005d
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteUnfinalizedExceptionTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.database.sqlite;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabaseTest.ClassToTestSqlCompilationAndCaching;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.File;
+
+public class SQLiteUnfinalizedExceptionTest extends AndroidTestCase {
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private static final String TABLE_NAME = "testCursor";
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
+        mDatabaseFile = new File(dbDir, "UnfinalizedExceptionTest.db");
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabase);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    @SmallTest
+    public void testUnfinalizedExceptionNotExcpected() {
+        mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
+        // the above statement should be in SQLiteDatabase.mPrograms
+        // and should automatically be finalized when database is closed
+        mDatabase.lock();
+        try {
+            mDatabase.closeDatabase();
+        } finally {
+            mDatabase.unlock();
+        }
+    }
+
+    @SmallTest
+    public void testUnfinalizedException() {
+        mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
+        mDatabase.lock();
+        mDatabase.closePendingStatements(); // clears the above from finalizer queue in mdatabase
+        mDatabase.unlock();
+        ClassToTestSqlCompilationAndCaching.create(mDatabase, "select * from "  + TABLE_NAME);
+        // since the above is NOT closed, closing database should fail
+        mDatabase.lock();
+        try {
+            mDatabase.closeDatabase();
+            fail("exception expected");
+        } catch (SQLiteUnfinalizedObjectsException e) {
+            // expected
+        } finally {
+            mDatabase.unlock();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index fe608b5..c8ad60d 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -285,6 +285,14 @@
         assertEquals("d", uri.getQueryParameter("c"));
     }
 
+    // http://b/2337042
+    @SmallTest
+    public void testHostWithTrailingDot() {
+        Uri uri = Uri.parse("http://google.com./b/c/g");
+        assertEquals("google.com.", uri.getHost());
+        assertEquals("/b/c/g", uri.getPath());
+    }
+
     @SmallTest
     public void testSchemeOnly() {
         Uri uri = Uri.parse("empty:");
diff --git a/core/tests/coretests/src/android/net/WebAddressTest.java b/core/tests/coretests/src/android/net/WebAddressTest.java
new file mode 100644
index 0000000..f0af35d
--- /dev/null
+++ b/core/tests/coretests/src/android/net/WebAddressTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.net;
+
+import android.net.WebAddress;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+public class WebAddressTest extends TestCase {
+
+    // http://b/2337042
+    @SmallTest
+    public void testHostWithTrailingDot() {
+        WebAddress webAddress = new WebAddress("http://google.com./b/c/g");
+        assertEquals("google.com.", webAddress.mHost);
+        assertEquals("/b/c/g", webAddress.mPath);
+    }
+
+    // http://b/1011602
+    @SmallTest
+    public void testPathWithoutLeadingSlash() {
+        WebAddress webAddress = new WebAddress("http://www.myspace.com?si=1");
+        assertEquals("www.myspace.com", webAddress.mHost);
+        assertEquals("/?si=1", webAddress.mPath);
+    }
+}
diff --git a/data/etc/android.hardware.nfc.xml b/data/etc/android.hardware.nfc.xml
new file mode 100644
index 0000000..81c4a84
--- /dev/null
+++ b/data/etc/android.hardware.nfc.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device can communicate
+     using Near-Field Communications (NFC). -->
+<permissions>
+    <feature name="android.hardware.nfc" />
+</permissions>
diff --git a/data/etc/android.hardware.sensor.barometer.xml b/data/etc/android.hardware.sensor.barometer.xml
new file mode 100644
index 0000000..ebd392d
--- /dev/null
+++ b/data/etc/android.hardware.sensor.barometer.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Feature for devices with barometer. -->
+<permissions>
+    <feature name="android.hardware.sensor.barometer" />
+</permissions>
diff --git a/data/etc/android.hardware.sensor.gyroscope.xml b/data/etc/android.hardware.sensor.gyroscope.xml
new file mode 100644
index 0000000..fe79632
--- /dev/null
+++ b/data/etc/android.hardware.sensor.gyroscope.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Feature for devices with gyroscope. -->
+<permissions>
+    <feature name="android.hardware.sensor.gyroscope" />
+</permissions>
diff --git a/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml b/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml
new file mode 100644
index 0000000..80bf859
--- /dev/null
+++ b/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard set of features for a touchscreen that supports
+     independently-trackable multiple-finger multitouch. -->
+<permissions>
+    <feature name="android.hardware.touchscreen" />
+    <feature name="android.hardware.touchscreen.multitouch" />
+    <feature name="android.hardware.touchscreen.multitouch.distinct" />
+    <feature name="android.hardware.touchscreen.multitouch.jazzhand" />
+</permissions>
diff --git a/data/etc/android.software.sip.voip.xml b/data/etc/android.software.sip.voip.xml
new file mode 100644
index 0000000..edd06c1
--- /dev/null
+++ b/data/etc/android.software.sip.voip.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<!-- This is the standard set of features for devices that support SIP-based VoIP. -->
+<permissions>
+    <feature name="android.software.sip" />
+    <feature name="android.software.sip.voip" />
+</permissions>
diff --git a/data/etc/android.software.sip.xml b/data/etc/android.software.sip.xml
new file mode 100644
index 0000000..d9fcaad
--- /dev/null
+++ b/data/etc/android.software.sip.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<!-- This is the standard set of features for devices that support the SIP API. -->
+<permissions>
+    <feature name="android.software.sip" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index efca4c2..a3c9f6d 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -38,10 +38,19 @@
     <!-- devices with a front facing camera must include
          android.hardware.camera.front.xml -->
     <!-- devices with WiFi must also include android.hardware.wifi.xml -->
-    <!-- devices with limited/gestural multitouch must also include
-         android.hardware.touchscreen.multitouch.xml -->
-    <!-- devices with full multitouch must also include
-         android.hardware.touchscreen.multitouch.distinct.xml -->
+    <!-- devices that support multitouch must include the most appropriate one
+         of these files:
+
+         If only partial (non-independent) pointers are supported:
+         android.hardware.touchscreen.multitouch.xml
+
+         If up to 4 independently tracked pointers are supported:
+         include android.hardware.touchscreen.multitouch.distinct.xml
+
+         If 5 or more independently tracked pointers are supported:
+         include android.hardware.touchscreen.multitouch.jazzhand.xml
+
+         ONLY ONE of the above should be included. -->
     <!-- devices with an ambient light sensor must also include
          android.hardware.sensor.light.xml -->
     <!-- devices with a proximity sensor must also include
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 0e5df8c..439fc90 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -94,8 +94,6 @@
     <assign-permission name="android.permission.CALL_PHONE" uid="shell" />
     <assign-permission name="android.permission.READ_CONTACTS" uid="shell" />
     <assign-permission name="android.permission.WRITE_CONTACTS" uid="shell" />
-    <assign-permission name="android.permission.READ_OWNER_DATA" uid="shell" />
-    <assign-permission name="android.permission.WRITE_OWNER_DATA" uid="shell" />
     <assign-permission name="android.permission.READ_CALENDAR" uid="shell" />
     <assign-permission name="android.permission.WRITE_CALENDAR" uid="shell" />
     <assign-permission name="android.permission.READ_USER_DICTIONARY" uid="shell" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
new file mode 100644
index 0000000..952d0782
--- /dev/null
+++ b/data/etc/tablet_core_hardware.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- These are the hardware components that all handheld devices
+     must include. Devices with optional hardware must also include extra
+     hardware files, per the comments below.
+
+     Handheld devices include phones, mobile Internet devices (MIDs),
+     Personal Media Players (PMPs), small tablets (7" or less), and similar
+     devices.
+-->
+<permissions>
+    <feature name="android.hardware.location" />
+    <feature name="android.hardware.location.network" />
+    <feature name="android.hardware.sensor.compass" />
+    <feature name="android.hardware.sensor.accelerometer" />
+    <feature name="android.hardware.bluetooth" />
+    <feature name="android.hardware.touchscreen" />
+    <feature name="android.hardware.touchscreen.multitouch" />
+    <feature name="android.hardware.touchscreen.multitouch.distinct" />
+    <feature name="android.hardware.microphone" />
+    <!-- devices with GPS must include android.hardware.location.gps.xml -->
+    <!-- devices with a rear-facing camera must include one of these as appropriate:
+         android.hardware.camera.xml or 
+         android.hardware.camera.autofocus.xml or 
+         android.hardware.camera.autofocus-flash.xml -->
+    <!-- devices with a front facing camera must include
+         android.hardware.camera.front.xml -->
+    <!-- devices with WiFi must also include android.hardware.wifi.xml -->
+    <!-- devices with an ambient light sensor must also include
+         android.hardware.sensor.light.xml -->
+    <!-- devices with a proximity sensor must also include
+         android.hardware.sensor.proximity.xml -->
+    <!-- devices with a barometer must also include
+         android.hardware.sensor.barometer.xml -->
+    <!-- devices with a gyroscope must also include
+         android.hardware.sensor.gyroscope.xml -->
+    <!-- GSM phones must also include android.hardware.telephony.gsm.xml -->
+    <!-- CDMA phones must also include android.hardware.telephony.cdma.xml -->
+</permissions>
diff --git a/docs/html/guide/practices/design/performance.jd b/docs/html/guide/practices/design/performance.jd
index f22d2d3..56872a7 100644
--- a/docs/html/guide/practices/design/performance.jd
+++ b/docs/html/guide/practices/design/performance.jd
@@ -42,188 +42,39 @@
 
 <h2 id="optimize_judiciously">Optimize Judiciously</h2>
 
-<p>As you get started thinking about how to design your application, and as
-you write it, consider
-the cautionary points about optimization that Josh Bloch makes in his book
-<em>Effective Java</em>. Here's "Item 47: Optimize Judiciously", excerpted from
-the latest edition of the book with permission. Although Josh didn't have
-Android application development in mind when writing this section &mdash; for
-example, the <code style="color:black">java.awt.Component</code> class
-referenced is not available in Android, and Android uses the
-Dalvik VM, rather than a standard JVM &mdash; his points are still valid. </p>
+<p>This document is about Android-specific micro-optimization, so it assumes
+that you've already used profiling to work out exactly what code needs to be
+optimized, and that you already have a way to measure the effect (good or bad)
+of any changes you make. You only have so much engineering time to invest, so
+it's important to know you're spending it wisely.
 
-<blockquote>
+<p>(See <a href="#closing_notes">Closing Notes</a> for more on profiling and
+writing effective benchmarks.)
 
-<p>There are three aphorisms concerning optimization that everyone should know.
-They are perhaps beginning to suffer from overexposure, but in case you aren't
-yet familiar with them, here they are:</p>
+<p>This document also assumes that you made the best decisions about data
+structures and algorithms, and that you've also considered the future
+performance consequences of your API decisions. Using the right data
+structures and algorithms will make more difference than any of the advice
+here, and considering the performance consequences of your API decisions will
+make it easier to switch to better implementations later (this is more
+important for library code than for application code).
 
-<div style="padding-left:3em;padding-right:4em;">
+<p>(If you need that kind of advice, see Josh Bloch's <em>Effective Java</em>,
+item 47.)</p>
 
-<p style="margin-bottom:.5em;">More computing sins are committed in the name of
-efficiency (without necessarily achieving it) than for any other single
-reason&mdash;including blind stupidity.</p>
-<p>&mdash;William A. Wulf <span style="font-size:80%;"><sup>1</sup></span></p>
+<p>One of the trickiest problems you'll face when micro-optimizing an Android
+app is that your app is pretty much guaranteed to be running on multiple
+hardware platforms. Different versions of the VM running on different
+processors running at different speeds. It's not even generally the case
+that you can simply say "device X is a factor F faster/slower than device Y",
+and scale your results from one device to others. In particular, measurement
+on the emulator tells you very little about performance on any device. There
+are also huge differences between devices with and without a JIT: the "best"
+code for a device with a JIT is not always the best code for a device
+without.</p>
 
-<p style="margin-bottom:.5em;">We should forget about small efficiencies, say
-about 97% of the time: premature optimization is the root of all evil. </p>
-<p>&mdash;Donald E. Knuth <span style="font-size:80%;"><sup>2</sup></span></p>
-
-
-<p style="margin-bottom:.5em;">We follow two rules in the matter of optimization:</p>
-<ul style="margin-bottom:0">
-<li>Rule 1. Don't do it.</li>
-<li>Rule 2 (for experts only). Don't do it yet &mdash; that is, not until you have a
-perfectly clear and unoptimized solution. </li>
-</ul>
-<p>&mdash;M. A. Jackson <span style="font-size:80%;"><sup>3</sup></span></p>
-</div>
-
-<p>All of these aphorisms predate the Java programming language by two decades.
-They tell a deep truth about optimization: it is easy to do more harm than good,
-especially if you optimize prematurely. In the process, you may produce software
-that is neither fast nor correct and cannot easily be fixed.</p>
-
-<p>Don't sacrifice sound architectural principles for performance.
-<strong>Strive to write good programs rather than fast ones.</strong> If a good
-program is not fast enough, its architecture will allow it to be optimized. Good
-programs embody the principle of <em>information hiding</em>: where possible,
-they localize design decisions within individual modules, so individual
-decisions can be changed without affecting the remainder of the system (Item
-13).</p>
-
-<p>This does <em>not</em> mean that you can ignore performance concerns until
-your program is complete. Implementation problems can be fixed by later
-optimization, but pervasive architectural flaws that limit performance can be
-impossible to fix without rewriting the system. Changing a fundamental facet of
-your design after the fact can result in an ill-structured system that is
-difficult to maintain and evolve. Therefore you must think about performance
-during the design process.</p>
-
-<p><strong>Strive to avoid design decisions that limit performance.</strong> The
-components of a design that are most difficult to change after the fact are
-those specifying interactions between modules and with the outside world. Chief
-among these design components are APIs, wire-level protocols, and persistent
-data formats. Not only are these design components difficult or impossible to
-change after the fact, but all of them can place significant limitations on the
-performance that a system can ever achieve.</p>
-
-<p><strong>Consider the performance consequences of your API design
-decisions.</strong> Making a public type mutable may require a lot of needless
-defensive copying (Item 39). Similarly, using inheritance in a public class
-where composition would have been appropriate ties the class forever to its
-superclass, which can place artificial limits on the performance of the subclass
-(Item 16). As a final example, using an implementation type rather than an
-interface in an API ties you to a specific implementation, even though faster
-implementations may be written in the future (Item 52).</p>
-
-<p>The effects of API design on performance are very real. Consider the <code
-style="color:black">getSize</code> method in the <code
-style="color:black">java.awt.Component</code> class. The decision that this
-performance-critical method was to return a <code
-style="color:black">Dimension</code> instance, coupled with the decision that
-<code style="color:black">Dimension</code> instances are mutable, forces any
-implementation of this method to allocate a new <code
-style="color:black">Dimension</code> instance on every invocation. Even though
-allocating small objects is inexpensive on a modern VM, allocating millions of
-objects needlessly can do real harm to performance.</p>
-
-<p>In this case, several alternatives existed. Ideally, <code
-style="color:black">Dimension</code> should have been immutable (Item 15);
-alternatively, the <code style="color:black">getSize</code> method could have
-been replaced by two methods returning the individual primitive components of a
-<code style="color:black">Dimension</code> object. In fact, two such methods
-were added to the Component API in the 1.2 release for performance reasons.
-Preexisting client code, however, still uses the <code
-style="color:black">getSize</code> method and still suffers the performance
-consequences of the original API design decisions.</p>
-
-<p>Luckily, it is generally the case that good API design is consistent with
-good performance. <strong>It is a very bad idea to warp an API to achieve good
-performance.</strong> The performance issue that caused you to warp the API may
-go away in a future release of the platform or other underlying software, but
-the warped API and the support headaches that come with it will be with you for
-life.</p>
-
-<p>Once you've carefully designed your program and produced a clear, concise,
-and well-structured implementation, <em>then</em> it may be time to consider
-optimization, assuming you're not already satisfied with the performance of the
-program.</p>
-
-<p>Recall that Jackson's two rules of optimization were "Don't do it," and "(for
-experts only). Don't do it yet." He could have added one more: <strong>measure
-performance before and after each attempted optimization.</strong> You may be
-surprised by what you find. Often, attempted optimizations have no measurable
-effect on performance; sometimes, they make it worse. The main reason is that
-it's difficult to guess where your program is spending its time. The part of the
-program that you think is slow may not be at fault, in which case you'd be
-wasting your time trying to optimize it. Common wisdom says that programs spend
-80 percent of their time in 20 percent of their code.</p>
-
-<p>Profiling tools can help you decide where to focus your optimization efforts.
-Such tools give you runtime information, such as roughly how much time each
-method is consuming and how many times it is invoked. In addition to focusing
-your tuning efforts, this can alert you to the need for algorithmic changes. If
-a quadratic (or worse) algorithm lurks inside your program, no amount of tuning
-will fix the problem. You must replace the algorithm with one that is more
-efficient. The more code in the system, the more important it is to use a
-profiler. It's like looking for a needle in a haystack: the bigger the haystack,
-the more useful it is to have a metal detector. The JDK comes with a simple
-profiler and modern IDEs provide more sophisticated profiling tools.</p>
-
-<p>The need to measure the effects of attempted optimization is even greater on
-the Java platform than on more traditional platforms, because the Java
-programming language does not have a strong <em>performance model</em>. The
-relative costs of the various primitive operations are not well defined. The
-"semantic gap" between what the programmer writes and what the CPU executes is
-far greater than in traditional statically compiled languages, which makes it
-very difficult to reliably predict the performance consequences of any
-optimization. There are plenty of performance myths floating around that turn
-out to be half-truths or outright lies.</p>
-
-<p>Not only is Java's performance model ill-defined, but it varies from JVM
-implementation to JVM implementation, from release to release, and from
-processor to processor. If you will be running your program on multiple JVM
-implementations or multiple hardware platforms, it is important that you measure
-the effects of your optimization on each. Occasionally you may be forced to make
-trade-offs between performance on different JVM implementations or hardware
-platforms.</p>
-
-<p>To summarize, do not strive to write fast programs &mdash; strive to write
-good ones; speed will follow. Do think about performance issues while you're
-designing systems and especially while you're designing APIs, wire-level
-protocols, and persistent data formats. When you've finished building the
-system, measure its performance. If it's fast enough, you're done. If not,
-locate the source of the problems with the aid of a profiler, and go to work
-optimizing the relevant parts of the system. The first step is to examine your
-choice of algorithms: no amount of low-level optimization can make up for a poor
-choice of algorithm. Repeat this process as necessary, measuring the performance
-after every change, until you're satisfied.</p>
-
-<p>&mdash;Excerpted from Josh Bloch's <em>Effective Java</em>, Second Ed.
-(Addison-Wesley, 2008).</em></p>
-
-<p style="font-size:80%;margin-bottom:0;"><sup>1</sup> Wulf, W. A Case Against
-the GOTO. <em>Proceedings of the 25th ACM National
-Conference</em> 2 (1972): 791–797.</p>
-<p style="font-size:80%;margin-bottom:0;"><sup>2</sup> Knuth, Donald. Structured
-Programming with go to Statements. <em>Computing
-Surveys 6</em> (1974): 261–301.</p>
-<p style="font-size:80%"><sup>3</sup> Jackson, M. A. <em>Principles of Program
-Design</em>, Academic Press, London, 1975.
-ISBN: 0123790506.</p>
-
-</blockquote>
-
-<p>One of the trickiest problems you'll face when micro-optimizing Android
-apps is that the "if you will be running your program on ... multiple hardware
-platforms" clause above is always true. And it's not even generally the case
-that you can say "device X is a factor F faster/slower than device Y".
-This is especially true if one of the devices is the emulator, or one of the
-devices has a JIT. If you want to know how your app performs on a given device,
-you need to test it on that device. Drawing conclusions from the emulator is
-particularly dangerous, as is attempting to compare JIT versus non-JIT
-performance: the performance <em>profiles</em> can differ wildly.</p>
+<p>If you want to know how your app performs on a given device, you need to
+test on that device.</p>
 
 <a name="object_creation"></a>
 <h2>Avoid Creating Objects</h2>
@@ -566,3 +417,11 @@
 not measuring what you think you're measuring (because, say, the VM has
 managed to optimize all your code away). We highly recommend you use Caliper
 to run your own microbenchmarks.</p>
+
+<p>You may also find
+<a href="{@docRoot}guide/developing/tools/traceview.html">Traceview</a> useful
+for profiling, but it's important to realize that it currently disables the JIT,
+which may cause it to misattribute time to code that the JIT may be able to win
+back. It's especially important after making changes suggested by Traceview
+data to ensure that the resulting code actually runs faster when run without
+Traceview.
diff --git a/docs/html/guide/topics/data/data-storage.jd b/docs/html/guide/topics/data/data-storage.jd
index 293a057..e20d1ed 100644
--- a/docs/html/guide/topics/data/data-storage.jd
+++ b/docs/html/guide/topics/data/data-storage.jd
@@ -115,7 +115,7 @@
     public static final String PREFS_NAME = "MyPrefsFile";
 
     &#64;Override
-    protected void onCreate(Bundle state){         
+    protected void onCreate(Bundle state){
        super.onCreate(state);
        . . .
 
@@ -374,7 +374,7 @@
 can execute a SQLite command to create tables in the database. For example:</p>
 
 <pre>
-public class MyDbOpenHelper extends SQLiteOpenHelper {
+public class DictionaryOpenHelper extends SQLiteOpenHelper {
 
     private static final int DATABASE_VERSION = 2;
     private static final String DICTIONARY_TABLE_NAME = "dictionary";
diff --git a/docs/html/guide/topics/resources/more-resources.jd b/docs/html/guide/topics/resources/more-resources.jd
index a647571..6cae1eb 100644
--- a/docs/html/guide/topics/resources/more-resources.jd
+++ b/docs/html/guide/topics/resources/more-resources.jd
@@ -216,10 +216,13 @@
 For example: 10px, 2in, 5sp. The following units of measure are supported by Android:</p>
 <dl>
   <dt>{@code dp}</dt>
-    <dd>Density-independent Pixels - an abstract unit that is based on the physical density of the screen.
-    These units are relative to a 160 dpi screen, so one dp is one pixel on a 160 dpi screen. The ratio of
-    dp-to-pixel will change with the screen density, but not necessarily in direct proportion. The
-      compiler accepts both "dip" and "dp", though "dp" is more consistent with "sp".</dd>
+    <dd>Density-independent Pixels - an abstract unit that is based on the physical density of the
+screen. These units are relative to a 160 dpi (dots per inch) screen, so <em>{@code 160dp} is
+always one inch</em> regardless of the screen density. The ratio of dp-to-pixel will change with the
+screen density, but not necessarily in direct proportion. You should use these units when specifying
+view dimensions in your layout, so the UI properly scales to render at the same actual size on
+different screens. (The compiler accepts both "dip" and "dp", though "dp" is more consistent with
+"sp".)</dd>
   <dt>{@code sp}</dt>
     <dd>Scale-independent Pixels - this is like the dp unit, but it is also scaled by the user's font
     size preference. It is recommend you use this unit when specifying font sizes, so they will be adjusted
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index ed09f89..05b2d60 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -27,6 +27,7 @@
     int mSize;
     Element[] mElements;
     String[] mElementNames;
+    int[] mArraySizes;
 
     DataType mType;
     DataKind mKind;
@@ -313,11 +314,12 @@
         return rs.mElement_MATRIX_2X2;
     }
 
-    Element(int id, RenderScript rs, Element[] e, String[] n) {
+    Element(int id, RenderScript rs, Element[] e, String[] n, int[] as) {
         super(id, rs);
         mSize = 0;
         mElements = e;
         mElementNames = n;
+        mArraySizes = as;
         for (int ct = 0; ct < mElements.length; ct++ ) {
             mSize += mElements[ct].mSize;
         }
@@ -441,6 +443,7 @@
         RenderScript mRS;
         Element[] mElements;
         String[] mElementNames;
+        int[] mArraySizes;
         int mCount;
 
         public Builder(RenderScript rs) {
@@ -448,35 +451,49 @@
             mCount = 0;
             mElements = new Element[8];
             mElementNames = new String[8];
+            mArraySizes = new int[8];
         }
 
-        public void add(Element element, String name) {
+        public void add(Element element, String name, int arraySize) {
+            if (arraySize < 1) {
+                throw new IllegalArgumentException("Array size cannot be less than 1.");
+            }
             if(mCount == mElements.length) {
                 Element[] e = new Element[mCount + 8];
                 String[] s = new String[mCount + 8];
+                int[] as = new int[mCount + 8];
                 System.arraycopy(mElements, 0, e, 0, mCount);
                 System.arraycopy(mElementNames, 0, s, 0, mCount);
+                System.arraycopy(mArraySizes, 0, as, 0, mCount);
                 mElements = e;
                 mElementNames = s;
+                mArraySizes = as;
             }
             mElements[mCount] = element;
             mElementNames[mCount] = name;
+            mArraySizes[mCount] = arraySize;
             mCount++;
         }
 
+        public void add(Element element, String name) {
+            add(element, name, 1);
+        }
+
         public Element create() {
             mRS.validate();
             Element[] ein = new Element[mCount];
             String[] sin = new String[mCount];
+            int[] asin = new int[mCount];
             java.lang.System.arraycopy(mElements, 0, ein, 0, mCount);
             java.lang.System.arraycopy(mElementNames, 0, sin, 0, mCount);
+            java.lang.System.arraycopy(mArraySizes, 0, asin, 0, mCount);
 
             int[] ids = new int[ein.length];
             for (int ct = 0; ct < ein.length; ct++ ) {
                 ids[ct] = ein[ct].mID;
             }
-            int id = mRS.nElementCreate2(ids, sin);
-            return new Element(id, mRS, ein, sin);
+            int id = mRS.nElementCreate2(ids, sin, asin);
+            return new Element(id, mRS, ein, sin, asin);
         }
     }
 
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 62d70a7..1f3e159 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -152,9 +152,9 @@
     synchronized int nElementCreate(int type, int kind, boolean norm, int vecSize) {
         return rsnElementCreate(mContext, type, kind, norm, vecSize);
     }
-    native int  rsnElementCreate2(int con, int[] elements, String[] names);
-    synchronized int nElementCreate2(int[] elements, String[] names) {
-        return rsnElementCreate2(mContext, elements, names);
+    native int  rsnElementCreate2(int con, int[] elements, String[] names, int[] arraySizes);
+    synchronized int nElementCreate2(int[] elements, String[] names, int[] arraySizes) {
+        return rsnElementCreate2(mContext, elements, names, arraySizes);
     }
     native void rsnElementGetNativeData(int con, int id, int[] elementData);
     synchronized void nElementGetNativeData(int id, int[] elementData) {
diff --git a/graphics/jni/Android.mk b/graphics/jni/Android.mk
index 8476be1..4c4a128 100644
--- a/graphics/jni/Android.mk
+++ b/graphics/jni/Android.mk
@@ -13,14 +13,13 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libandroid_runtime \
-        libacc \
         libnativehelper \
         libRS \
         libcutils \
         libskia \
         libutils \
         libui \
-        libsurfaceflinger_client 
+        libsurfaceflinger_client
 
 LOCAL_STATIC_LIBRARIES :=
 
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index c8e6656..f07dbfd8 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -253,12 +253,13 @@
 }
 
 static jint
-nElementCreate2(JNIEnv *_env, jobject _this, RsContext con, jintArray _ids, jobjectArray _names)
+nElementCreate2(JNIEnv *_env, jobject _this, RsContext con, jintArray _ids, jobjectArray _names, jintArray _arraySizes)
 {
     int fieldCount = _env->GetArrayLength(_ids);
     LOG_API("nElementCreate2, con(%p)", con);
 
     jint *ids = _env->GetIntArrayElements(_ids, NULL);
+    jint *arraySizes = _env->GetIntArrayElements(_arraySizes, NULL);
     const char ** nameArray = (const char **)calloc(fieldCount, sizeof(char *));
     size_t* sizeArray = (size_t*)calloc(fieldCount, sizeof(size_t));
 
@@ -267,12 +268,13 @@
         nameArray[ct] = _env->GetStringUTFChars(s, NULL);
         sizeArray[ct] = _env->GetStringUTFLength(s);
     }
-    jint id = (jint)rsElementCreate2(con, fieldCount, (RsElement *)ids, nameArray, sizeArray);
+    jint id = (jint)rsElementCreate2(con, fieldCount, (RsElement *)ids, nameArray, sizeArray, (const uint32_t *)arraySizes);
     for (int ct=0; ct < fieldCount; ct++) {
         jstring s = (jstring)_env->GetObjectArrayElement(_names, ct);
         _env->ReleaseStringUTFChars(s, nameArray[ct]);
     }
     _env->ReleaseIntArrayElements(_ids, ids, JNI_ABORT);
+    _env->ReleaseIntArrayElements(_arraySizes, arraySizes, JNI_ABORT);
     free(nameArray);
     free(sizeArray);
     return (jint)id;
@@ -1230,7 +1232,7 @@
 {"rsnFontCreateFromFile",            "(ILjava/lang/String;II)I",             (void*)nFontCreateFromFile },
 
 {"rsnElementCreate",                 "(IIIZI)I",                              (void*)nElementCreate },
-{"rsnElementCreate2",                "(I[I[Ljava/lang/String;)I",             (void*)nElementCreate2 },
+{"rsnElementCreate2",                "(I[I[Ljava/lang/String;[I)I",           (void*)nElementCreate2 },
 {"rsnElementGetNativeData",          "(II[I)V",                               (void*)nElementGetNativeData },
 {"rsnElementGetSubElements",         "(II[I[Ljava/lang/String;)V",           (void*)nElementGetSubElements },
 
diff --git a/graphics/tests/graphicstests/src/android/graphics/BitmapFactoryTest.java b/graphics/tests/graphicstests/src/android/graphics/BitmapFactoryTest.java
new file mode 100644
index 0000000..09820ef
--- /dev/null
+++ b/graphics/tests/graphicstests/src/android/graphics/BitmapFactoryTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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 android.os.ParcelFileDescriptor;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+
+import junit.framework.TestCase;
+
+
+public class BitmapFactoryTest extends TestCase {
+
+    // tests that we can decode bitmaps from MemoryFiles
+    @SmallTest
+    public void testBitmapParcelFileDescriptor() throws Exception {
+        Bitmap bitmap1 = Bitmap.createBitmap(
+                new int[] { Color.BLUE }, 1, 1, Bitmap.Config.RGB_565);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        bitmap1.compress(Bitmap.CompressFormat.PNG, 100, out);
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromData(out.toByteArray(), null);
+        FileDescriptor fd = pfd.getFileDescriptor();
+        assertNotNull("Got null FileDescriptor", fd);
+        assertTrue("Got invalid FileDescriptor", fd.valid());
+        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd);
+        assertNotNull("BitmapFactory returned null", bitmap);
+        assertEquals("Bitmap width", 1, bitmap.getWidth());
+        assertEquals("Bitmap height", 1, bitmap.getHeight());
+    }
+
+}
diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h
index c388ba8..fdceb84 100644
--- a/include/android_runtime/android_app_NativeActivity.h
+++ b/include/android_runtime/android_app_NativeActivity.h
@@ -69,7 +69,7 @@
     /* Destroys the consumer and releases its input channel. */
     ~AInputQueue();
 
-    void attachLooper(ALooper* looper, ALooper_callbackFunc* callback, void* data);
+    void attachLooper(ALooper* looper, int ident, ALooper_callbackFunc* callback, void* data);
 
     void detachLooper();
 
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 2f61cbe..1f8ce71 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -19,6 +19,7 @@
 #define ANDROID_IOMX_H_
 
 #include <binder/IInterface.h>
+#include <ui/GraphicBuffer.h>
 #include <utils/List.h>
 #include <utils/String8.h>
 
@@ -78,10 +79,17 @@
             node_id node, OMX_INDEXTYPE index,
             const void *params, size_t size) = 0;
 
+    virtual status_t enableGraphicBuffers(
+            node_id node, OMX_U32 port_index, OMX_BOOL enable) = 0;
+
     virtual status_t useBuffer(
             node_id node, OMX_U32 port_index, const sp<IMemory> &params,
             buffer_id *buffer) = 0;
 
+    virtual status_t useGraphicBuffer(
+            node_id node, OMX_U32 port_index,
+            const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) = 0;
+
     // This API clearly only makes sense if the caller lives in the
     // same process as the callee, i.e. is the media_server, as the
     // returned "buffer_data" pointer is just that, a pointer into local
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 1af4254..a5cec78 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -72,6 +72,7 @@
     int64_t mPrevSampleTimeUs;
     int64_t mTotalLostFrames;
     int64_t mPrevLostBytes;
+    int64_t mInitialReadTimeUs;
 
     MediaBufferGroup *mGroup;
 
diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h
index 7135a33..e1cd0c8 100644
--- a/include/media/stagefright/CameraSourceTimeLapse.h
+++ b/include/media/stagefright/CameraSourceTimeLapse.h
@@ -31,13 +31,12 @@
 
 class CameraSourceTimeLapse : public CameraSource {
 public:
-    static CameraSourceTimeLapse *Create(bool useStillCameraForTimeLapse,
+    static CameraSourceTimeLapse *Create(
         int64_t timeBetweenTimeLapseFrameCaptureUs,
         int32_t width, int32_t height,
         int32_t videoFrameRate);
 
     static CameraSourceTimeLapse *CreateFromCamera(const sp<Camera> &camera,
-        bool useStillCameraForTimeLapse,
         int64_t timeBetweenTimeLapseFrameCaptureUs,
         int32_t width, int32_t height,
         int32_t videoFrameRate);
@@ -87,7 +86,6 @@
     bool mCameraIdle;
 
     CameraSourceTimeLapse(const sp<Camera> &camera,
-        bool useStillCameraForTimeLapse,
         int64_t timeBetweenTimeLapseFrameCaptureUs,
         int32_t width, int32_t height,
         int32_t videoFrameRate);
@@ -124,6 +122,11 @@
     virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
             const sp<IMemory> &data);
 
+    // If the passed in size (width x height) is a supported preview size,
+    // the function sets the camera's preview size to it and returns true.
+    // Otherwise returns false.
+    bool trySettingPreviewSize(int32_t width, int32_t height);
+
     // The still camera may not support the demanded video width and height.
     // We look for the supported picture sizes from the still camera and
     // choose the smallest one with either dimensions higher than the corresponding
diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h
index 221c679..4ded5e8 100644
--- a/include/media/stagefright/HardwareAPI.h
+++ b/include/media/stagefright/HardwareAPI.h
@@ -21,10 +21,60 @@
 #include <media/stagefright/OMXPluginBase.h>
 #include <media/stagefright/VideoRenderer.h>
 #include <surfaceflinger/ISurface.h>
+#include <ui/android_native_buffer.h>
 #include <utils/RefBase.h>
 
 #include <OMX_Component.h>
 
+namespace android {
+
+// A pointer to this struct is passed to the OMX_SetParameter when the extension
+// index for the 'OMX.google.android.index.enableAndroidNativeBuffers' extension
+// is given.
+//
+// When Android native buffer use is disabled for a port (the default state),
+// the OMX node should operate as normal, and expect UseBuffer calls to set its
+// buffers.  This is the mode that will be used when CPU access to the buffer is
+// required.
+//
+// When Android native buffer use has been enabled, the OMX node must support
+// only color formats in the range [OMX_COLOR_FormatAndroidPrivateStart,
+// OMX_COLOR_FormatAndroidPrivateEnd).  The node should then expect to receive
+// UseAndroidNativeBuffer calls (via OMX_SetParameter) rather than UseBuffer
+// calls.
+struct EnableAndroidNativeBuffersParams {
+    OMX_U32 nSize;
+    OMX_VERSIONTYPE nVersion;
+    OMX_U32 nPortIndex;
+    OMX_BOOL enable;
+};
+
+// Color formats in the range [OMX_COLOR_FormatAndroidPrivateStart,
+// OMX_COLOR_FormatAndroidPrivateEnd) will be converted to a gralloc pixel
+// format when used to allocate Android native buffers via gralloc.  The
+// conversion is done by subtracting OMX_COLOR_FormatAndroidPrivateStart from
+// the color format reported by the codec.
+enum {
+    OMX_COLOR_FormatAndroidPrivateStart = 0xA0000000,
+    OMX_COLOR_FormatAndroidPrivateEnd = 0xB0000000,
+};
+
+// A pointer to this struct is passed to OMX_SetParameter when the extension
+// index for the 'OMX.google.android.index.useAndroidNativeBuffer' extension is
+// given.  This call will only be performed if a prior call was made with the
+// 'OMX.google.android.index.enableAndroidNativeBuffers' extension index,
+// enabling use of Android native buffers.
+struct UseAndroidNativeBufferParams {
+    OMX_U32 nSize;
+    OMX_VERSIONTYPE nVersion;
+    OMX_U32 nPortIndex;
+    OMX_PTR pAppPrivate;
+    OMX_BUFFERHEADERTYPE **bufferHeader;
+    const sp<android_native_buffer_t>& nativeBuffer;
+};
+
+}  // namespace android
+
 extern android::VideoRenderer *createRenderer(
         const android::sp<android::ISurface> &surface,
         const char *componentName,
@@ -35,4 +85,3 @@
 extern android::OMXPluginBase *createOMXPlugin();
 
 #endif  // HARDWARE_API_H_
-
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 2412f6a..70bd8e8 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -62,6 +62,7 @@
     class Track;
 
     FILE *mFile;
+    bool mUse4ByteNalLength;
     bool mUse32BitOffset;
     bool mPaused;
     bool mStarted;
@@ -132,9 +133,13 @@
     // Adjust other track media clock (presumably wall clock)
     // based on audio track media clock with the drift time.
     int64_t mDriftTimeUs;
-    void addDriftTimeUs(int64_t driftTimeUs);
+    void setDriftTimeUs(int64_t driftTimeUs);
     int64_t getDriftTimeUs();
 
+    // Return whether the nal length is 4 bytes or 2 bytes
+    // Only makes sense for H.264/AVC
+    bool useNalLengthFour();
+
     void lock();
     void unlock();
 
@@ -144,6 +149,7 @@
 
     inline size_t write(const void *ptr, size_t size, size_t nmemb, FILE* stream);
     bool exceedsFileSizeLimit();
+    bool use32BitFileOffset() const;
     bool exceedsFileDurationLimit();
     void trackProgressStatus(const Track* track, int64_t timeUs, status_t err = OK);
 
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 43354c2..1e447f1 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -48,6 +48,7 @@
     kKeyTime              = 'time',  // int64_t (usecs)
     kKeyNTPTime           = 'ntpT',  // uint64_t (ntp-timestamp)
     kKeyTargetTime        = 'tarT',  // int64_t (usecs)
+    kKeyDriftTime         = 'dftT',  // int64_t (usecs)
     kKeyDuration          = 'dura',  // int64_t (usecs)
     kKeyColorFormat       = 'colf',
     kKeyPlatformPrivate   = 'priv',  // pointer
@@ -78,6 +79,7 @@
 
     // Set this key to enable authoring files in 64-bit offset
     kKey64BitFileOffset   = 'fobt',  // int32_t (bool)
+    kKey2ByteNalLength    = '2NAL',  // int32_t (bool)
 
     // Identify the file output format for authoring
     // Please see <media/mediarecorder.h> for the supported
@@ -90,6 +92,8 @@
 
     kKeyNotRealTime       = 'ntrt',  // bool (int32_t)
 
+    // Ogg files can be tagged to be automatically looping...
+    kKeyAutoLoop          = 'autL',  // bool (int32_t)
 };
 
 enum {
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 3fa825f..b587e94 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -78,6 +78,11 @@
 
     POLICY_FLAG_RAW_MASK = 0x0000ffff,
 
+    /* These flags are set by the input dispatcher. */
+
+    // Indicates that the input event was injected.
+    POLICY_FLAG_INJECTED = 0x01000000,
+
     /* These flags are set by the input reader policy as it intercepts each event. */
 
     // Indicates that the screen was off when the event was received and the event
@@ -225,6 +230,8 @@
 
     inline int32_t getAction() const { return mAction; }
 
+    inline int32_t getFlags() const { return mFlags; }
+
     inline int32_t getEdgeFlags() const { return mEdgeFlags; }
 
     inline int32_t getMetaState() const { return mMetaState; }
@@ -343,6 +350,7 @@
             int32_t deviceId,
             int32_t source,
             int32_t action,
+            int32_t flags,
             int32_t edgeFlags,
             int32_t metaState,
             float xOffset,
@@ -370,6 +378,7 @@
 
 private:
     int32_t mAction;
+    int32_t mFlags;
     int32_t mEdgeFlags;
     int32_t mMetaState;
     float mXOffset;
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index aed4fa1..f00f2db 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -84,14 +84,22 @@
          * current event is delivered to this target or a timeout occurs. */
         FLAG_SYNC = 0x01,
 
-        /* This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
-         * this target and so should instead be delivered as an ACTION_OUTSIDE to this target. */
+        /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
+         * of the area of this target and so should instead be delivered as an
+         * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
         FLAG_OUTSIDE = 0x02,
 
         /* This flag indicates that a KeyEvent or MotionEvent is being canceled.
-         * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
-         * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL. */
-        FLAG_CANCEL = 0x04
+         * In the case of a key event, it should be delivered with flag
+         * AKEY_EVENT_FLAG_CANCELED set.
+         * In the case of a motion event, it should be delivered with action
+         * AMOTION_EVENT_ACTION_CANCEL instead. */
+        FLAG_CANCEL = 0x04,
+
+        /* This flag indicates that the target of a MotionEvent is partly or wholly
+         * obscured by another visible window above it.  The motion event should be
+         * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
+        FLAG_WINDOW_IS_OBSCURED = 0x08,
     };
 
     // The input channel to be targeted.
@@ -139,9 +147,12 @@
     /* Notifies the system that an input channel recovered from ANR. */
     virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;
 
-    /* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */
+    /* Gets the key repeat initial timeout or -1 if automatic key repeating is disabled. */
     virtual nsecs_t getKeyRepeatTimeout() = 0;
 
+    /* Gets the key repeat inter-key delay. */
+    virtual nsecs_t getKeyRepeatDelay() = 0;
+
     /* Waits for key event input targets to become available.
      * If the event is being injected, injectorPid and injectorUid should specify the
      * process id and used id of the injecting application, otherwise they should both
@@ -193,7 +204,8 @@
             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
             int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
     virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
-            uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags,
+            uint32_t policyFlags, int32_t action, int32_t flags,
+            int32_t metaState, int32_t edgeFlags,
             uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
             float xPrecision, float yPrecision, nsecs_t downTime) = 0;
 
@@ -257,7 +269,8 @@
             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
             int32_t scanCode, int32_t metaState, nsecs_t downTime);
     virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
-            uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags,
+            uint32_t policyFlags, int32_t action, int32_t flags,
+            int32_t metaState, int32_t edgeFlags,
             uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
             float xPrecision, float yPrecision, nsecs_t downTime);
 
@@ -327,6 +340,7 @@
         int32_t source;
         uint32_t policyFlags;
         int32_t action;
+        int32_t flags;
         int32_t metaState;
         int32_t edgeFlags;
         float xPrecision;
@@ -458,7 +472,8 @@
                 int32_t repeatCount, nsecs_t downTime);
         MotionEntry* obtainMotionEntry(nsecs_t eventTime,
                 int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action,
-                int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
+                int32_t flags, int32_t metaState, int32_t edgeFlags,
+                float xPrecision, float yPrecision,
                 nsecs_t downTime, uint32_t pointerCount,
                 const int32_t* pointerIds, const PointerCoords* pointerCoords);
         DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry);
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index 31ec701..82831e29 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -135,6 +135,7 @@
 
         struct {
             int32_t action;
+            int32_t flags;
             int32_t metaState;
             int32_t edgeFlags;
             nsecs_t downTime;
@@ -218,6 +219,7 @@
             int32_t deviceId,
             int32_t source,
             int32_t action,
+            int32_t flags,
             int32_t edgeFlags,
             int32_t metaState,
             float xOffset,
diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h
index 81230e8..bc616eb 100644
--- a/include/utils/PollLoop.h
+++ b/include/utils/PollLoop.h
@@ -111,12 +111,18 @@
      * This method can be called on any thread.
      * This method may block briefly if it needs to wake the poll loop.
      */
-    void setCallback(int fd, int events, Callback callback, void* data = NULL);
+    void setCallback(int fd, int ident, int events, Callback callback, void* data = NULL);
 
     /**
+     * Convenience for above setCallback when ident is not used.  In this case
+     * the ident is set to POLL_CALLBACK.
+     */
+    void setCallback(int fd, int events, Callback callback, void* data = NULL);
+    
+    /**
      * Like setCallback(), but for the NDK callback function.
      */
-    void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback,
+    void setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback,
             void* data);
     
     /**
@@ -153,11 +159,13 @@
     struct RequestedCallback {
         Callback callback;
         ALooper_callbackFunc* looperCallback;
+        int ident;
         void* data;
     };
 
     struct PendingCallback {
         int fd;
+        int ident;
         int events;
         Callback callback;
         ALooper_callbackFunc* looperCallback;
@@ -185,7 +193,7 @@
     void openWakePipe();
     void closeWakePipe();
 
-    void setCallbackCommon(int fd, int events, Callback callback,
+    void setCallbackCommon(int fd, int ident, int events, Callback callback,
             ALooper_callbackFunc* looperCallback, void* data);
     ssize_t getRequestIndexLocked(int fd);
     void wakeAndLock();
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
index 3396f25..7eb6da5 100644
--- a/libs/gui/SensorEventQueue.cpp
+++ b/libs/gui/SensorEventQueue.cpp
@@ -86,7 +86,7 @@
     Mutex::Autolock _l(mLock);
     if (mPollLoop == 0) {
         mPollLoop = new PollLoop(true);
-        mPollLoop->setCallback(getFd(), POLLIN, NULL, NULL);
+        mPollLoop->setCallback(getFd(), getFd(), POLLIN, NULL, NULL);
     }
     return mPollLoop;
 }
diff --git a/libs/hwui/GenerationCache.h b/libs/hwui/GenerationCache.h
index c358c80..c42a5d8 100644
--- a/libs/hwui/GenerationCache.h
+++ b/libs/hwui/GenerationCache.h
@@ -143,11 +143,7 @@
     }
 
     ssize_t index = mCache.indexOfKey(key);
-    if (index >= 0) {
-        sp<Entry<K, V> > entry = mCache.valueAt(index);
-        detachFromCache(entry);
-        addToCache(entry, key, value);
-    } else {
+    if (index < 0) {
         sp<Entry<K, V> > entry = new Entry<K, V>;
         addToCache(entry, key, value);
     }
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index e874a16..6dad831 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_UI_PATCH_CACHE_H
 #define ANDROID_UI_PATCH_CACHE_H
 
+#include <utils/ResourceTypes.h>
+
 #include "Patch.h"
 #include "GenerationCache.h"
 
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 90f548b..817f143 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -28,6 +28,7 @@
 struct Texture {
     Texture() {
         cleanup = false;
+        bitmapSize = 0;
     }
 
     /**
@@ -54,6 +55,10 @@
      * Indicates whether this texture should be cleaned up after use.
      */
     bool cleanup;
+    /**
+     * Optional, size of the original bitmap.
+     */
+    uint32_t bitmapSize;
 }; // struct Texture
 
 class AutoTexture {
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 753c544..e558870 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -18,6 +18,8 @@
 
 #include <GLES2/gl2.h>
 
+#include <utils/threads.h>
+
 #include "TextureCache.h"
 #include "Properties.h"
 
@@ -49,6 +51,7 @@
 }
 
 TextureCache::~TextureCache() {
+    Mutex::Autolock _l(mLock);
     mCache.clear();
 }
 
@@ -64,14 +67,17 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 uint32_t TextureCache::getSize() {
+    Mutex::Autolock _l(mLock);
     return mSize;
 }
 
 uint32_t TextureCache::getMaxSize() {
+    Mutex::Autolock _l(mLock);
     return mMaxSize;
 }
 
 void TextureCache::setMaxSize(uint32_t maxSize) {
+    Mutex::Autolock _l(mLock);
     mMaxSize = maxSize;
     while (mSize > mMaxSize) {
         mCache.removeOldest();
@@ -83,12 +89,9 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
-    if (bitmap) {
-        const uint32_t size = bitmap->rowBytes() * bitmap->height();
-        mSize -= size;
-    }
-
+    // This will be called already locked
     if (texture) {
+        mSize -= texture->bitmapSize;
         glDeleteTextures(1, &texture->id);
         delete texture;
     }
@@ -99,6 +102,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 Texture* TextureCache::get(SkBitmap* bitmap) {
+    Mutex::Autolock _l(mLock);
+
     Texture* texture = mCache.get(bitmap);
     if (!texture) {
         if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
@@ -115,6 +120,7 @@
         }
 
         texture = new Texture;
+        texture->bitmapSize = size;
         generateTexture(bitmap, texture, false);
 
         if (size < mMaxSize) {
@@ -131,15 +137,18 @@
 }
 
 void TextureCache::remove(SkBitmap* bitmap) {
+    Mutex::Autolock _l(mLock);
     mCache.remove(bitmap);
 }
 
 void TextureCache::clear() {
+    Mutex::Autolock _l(mLock);
     mCache.clear();
 }
 
 void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) {
     SkAutoLockPixels alp(*bitmap);
+
     if (!bitmap->readyToDraw()) {
         LOGE("Cannot generate texture from bitmap");
         return;
@@ -159,6 +168,7 @@
     switch (bitmap->getConfig()) {
     case SkBitmap::kA8_Config:
         texture->blend = true;
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
         glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height, 0,
                 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap->getPixels());
         break;
@@ -175,6 +185,7 @@
         texture->blend = !bitmap->isOpaque();
         break;
     default:
+        LOGW("Unsupported bitmap config");
         break;
     }
 
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index b5e4c7c..847d69c 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -86,6 +86,8 @@
     uint32_t mSize;
     uint32_t mMaxSize;
     GLint mMaxTextureSize;
+
+    mutable Mutex mLock;
 }; // class TextureCache
 
 }; // namespace uirenderer
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 4a4d837..75b8d33 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -42,8 +42,8 @@
     private Bitmap mBitmapOut;
     private Bitmap mBitmapScratch;
     private ScriptC_Threshold mScript;
-    private ScriptC_Vertical_blur mScriptVBlur;
-    private ScriptC_Horizontal_blur mScriptHBlur;
+    private ScriptC_VerticalBlur mScriptVBlur;
+    private ScriptC_HorizontalBlur mScriptHBlur;
     private int mRadius = 0;
     private SeekBar mRadiusSeekBar;
 
@@ -380,8 +380,8 @@
         mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
         mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
 
-        mScriptVBlur = new ScriptC_Vertical_blur(mRS, getResources(), R.raw.vertical_blur, false);
-        mScriptHBlur = new ScriptC_Horizontal_blur(mRS, getResources(), R.raw.horizontal_blur, false);
+        mScriptVBlur = new ScriptC_VerticalBlur(mRS, getResources(), R.raw.verticalblur, false);
+        mScriptHBlur = new ScriptC_HorizontalBlur(mRS, getResources(), R.raw.horizontalblur, false);
 
         mScript = new ScriptC_Threshold(mRS, getResources(), R.raw.threshold, false);
         mScript.set_width(mBitmapIn.getWidth());
diff --git a/libs/rs/java/ModelViewer/res/raw/robot.a3d b/libs/rs/java/ModelViewer/res/raw/robot.a3d
index 2d7d32b..f48895c 100644
--- a/libs/rs/java/ModelViewer/res/raw/robot.a3d
+++ b/libs/rs/java/ModelViewer/res/raw/robot.a3d
Binary files differ
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
index 9672a6a..85c1d42 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
@@ -105,7 +105,7 @@
         bs.setMin(Sampler.Value.LINEAR);
         bs.setMag(Sampler.Value.LINEAR);
         bs.setWrapS(Sampler.Value.CLAMP);
-        bs.setWrapT(Sampler.Value.WRAP);
+        bs.setWrapT(Sampler.Value.CLAMP);
         mSampler = bs.create();
 
         ProgramFragment.Builder b = new ProgramFragment.Builder(mRS);
@@ -123,7 +123,6 @@
 
         mPVA = new ProgramVertex.MatrixAllocation(mRS);
         mPVBackground.bindAllocation(mPVA);
-        mPVA.setupProjectionNormalized(mWidth, mHeight);
 
         mScript.set_gPVBackground(mPVBackground);
     }
@@ -159,14 +158,14 @@
         mGroup1.addChild(mRobot1);
         mGroup1.addChild(mRobot2);
 
-        mGroup1.setTransform(0, new Float4(0.0f, 0.0f, 5.0f, 0.0f), TransformType.TRANSLATE);
+        mGroup1.setTransform(0, new Float4(0.0f, 0.0f, -15.0f, 0.0f), TransformType.TRANSLATE);
         mGroup1.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, 15.0f), TransformType.ROTATE);
 
-        mRobot1.setTransform(0, new Float4(-2.0f, -0.5f, 0.0f, 0.0f), TransformType.TRANSLATE);
+        mRobot1.setTransform(0, new Float4(-3.0f, -0.5f, 0.0f, 0.0f), TransformType.TRANSLATE);
         mRobot1.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, 20.0f), TransformType.ROTATE);
         mRobot1.setTransform(2, new Float4(0.2f, 0.2f, 0.2f, 0.0f), TransformType.SCALE);
 
-        mRobot2.setTransform(0, new Float4(2.0f, 0.0f, 0.0f, 0.0f), TransformType.TRANSLATE);
+        mRobot2.setTransform(0, new Float4(3.0f, 0.0f, 0.0f, 0.0f), TransformType.TRANSLATE);
         mRobot2.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, -20.0f), TransformType.ROTATE);
         mRobot2.setTransform(2, new Float4(0.3f, 0.3f, 0.3f, 0.0f), TransformType.SCALE);
     }
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
index b8b0119..d8d1a6e 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
@@ -79,7 +79,7 @@
             mRotation += 360;
         }
 
-        mScript.set_gRotate(-(float)mRotation);
+        mScript.set_gRotate((float)mRotation);
 
         mLastX = x;
         mLastY = y;
@@ -101,7 +101,7 @@
         bs.setMin(Sampler.Value.LINEAR);
         bs.setMag(Sampler.Value.LINEAR);
         bs.setWrapS(Sampler.Value.CLAMP);
-        bs.setWrapT(Sampler.Value.WRAP);
+        bs.setWrapT(Sampler.Value.CLAMP);
         mSampler = bs.create();
 
         ProgramFragment.Builder b = new ProgramFragment.Builder(mRS);
@@ -119,7 +119,6 @@
 
         mPVA = new ProgramVertex.MatrixAllocation(mRS);
         mPVBackground.bindAllocation(mPVA);
-        mPVA.setupProjectionNormalized(mWidth, mHeight);
 
         mScript.set_gPVBackground(mPVBackground);
     }
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
index c794438..ce6bb1e 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
@@ -71,6 +71,10 @@
     rsgClearDepth(1.0f);
 
     rsgBindProgramVertex(gPVBackground);
+    rs_matrix4x4 proj;
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
 
     rsgBindProgramFragment(gPFBackground);
     rsgBindProgramStore(gPFSBackground);
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs
index adb609c..43be266 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs
@@ -46,6 +46,10 @@
     rsgClearDepth(1.0f);
 
     rsgBindProgramVertex(gPVBackground);
+    rs_matrix4x4 proj;
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
 
     rsgBindProgramFragment(gPFBackground);
     rsgBindProgramStore(gPFSBackground);
@@ -54,9 +58,9 @@
     rs_matrix4x4 matrix;
     rsMatrixLoadIdentity(&matrix);
     // Position our model on the screen
-    rsMatrixTranslate(&matrix, 0.0f, -0.3f, 1.2f);
+    rsMatrixTranslate(&matrix, 0.0f, -0.3f, -10.0f);
     rsMatrixScale(&matrix, 0.2f, 0.2f, 0.2f);
-    rsMatrixRotate(&matrix, -25.0f, 1.0f, 0.0f, 0.0f);
+    rsMatrixRotate(&matrix, 25.0f, 1.0f, 0.0f, 0.0f);
     rsMatrixRotate(&matrix, gRotate, 0.0f, 1.0f, 0.0f);
     rsgProgramVertexLoadModelMatrix(&matrix);
 
diff --git a/libs/rs/java/Samples/res/raw/torus.a3d b/libs/rs/java/Samples/res/raw/torus.a3d
index d09bc13..0322b01 100644
--- a/libs/rs/java/Samples/res/raw/torus.a3d
+++ b/libs/rs/java/Samples/res/raw/torus.a3d
Binary files differ
diff --git a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
index 91613cf..4bcf7f5 100644
--- a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
+++ b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
@@ -369,6 +369,7 @@
     gVSConstants->light1_Diffuse = 1.0f;
     gVSConstants->light1_Specular = 0.7f;
     gVSConstants->light1_CosinePower = 50.0f;
+    rsAllocationMarkDirty(rsGetAllocation(gVSConstants));
 
     // Update fragmetn shader constants
     // Set light 0 colors
@@ -377,6 +378,7 @@
     // Set light 1 colors
     gFSConstants->light1_DiffuseColor = light1DiffCol;
     gFSConstants->light1_SpecularColor = light1SpecCol;
+    rsAllocationMarkDirty(rsGetAllocation(gFSConstants));
 }
 
 void displayCustomShaderSamples() {
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
index c1a16dd..eb1e48b 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
@@ -30,10 +30,10 @@
     private Resources mRes;
     private RenderScriptGL mRS;
 
-    private ScriptC_Test_root mRootScript;
+    private ScriptC_TestRoot mRootScript;
 
     private boolean fp_mad() {
-        ScriptC_Fp_mad s = new ScriptC_Fp_mad(mRS, mRes, R.raw.fp_mad, true);
+        ScriptC_FpMad s = new ScriptC_FpMad(mRS, mRes, R.raw.fpmad, true);
         s.invoke_doTest(0, 0);
         return true;
     }
@@ -43,7 +43,7 @@
         mRS = rs;
         mRes = res;
 
-        mRootScript = new ScriptC_Test_root(mRS, mRes, R.raw.test_root, true);
+        mRootScript = new ScriptC_TestRoot(mRS, mRes, R.raw.testroot, true);
 
         fp_mad();
 
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index ad162bb..c7fb2af 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -80,6 +80,7 @@
 	param const RsElement * elements
 	param const char ** names
 	param const size_t * nameLengths
+	param const uint32_t * arraySize
 	ret RsElement
 	}
 
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index 967f220..b6b5d2f 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -89,9 +89,9 @@
     void incRefs(const void *ptr, size_t ct) const;
     void decRefs(const void *ptr, size_t ct) const;
 
-protected:
     void sendDirty() const;
 
+protected:
     ObjectBaseRef<const Type> mType;
     void * mPtr;
 
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 2602dd4..0b7bb27 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -65,7 +65,7 @@
 
     size_t total = 0;
     for (size_t ct=0; ct < mFieldCount; ct++) {
-        total += mFields[ct].e->mBits;
+        total += mFields[ct].e->mBits * mFields[ct].arraySize;;
     }
     return total;
 }
@@ -95,6 +95,7 @@
     stream->addU32(mFieldCount);
     for(uint32_t ct = 0; ct < mFieldCount; ct++) {
         stream->addString(&mFields[ct].name);
+        stream->addU32(mFields[ct].arraySize);
         mFields[ct].e->serialize(stream);
     }
 }
@@ -122,6 +123,7 @@
         elem->mFields = new ElementField_t [elem->mFieldCount];
         for(uint32_t ct = 0; ct < elem->mFieldCount; ct ++) {
             stream->loadString(&elem->mFields[ct].name);
+            elem->mFields[ct].arraySize = stream->loadU32();
             Element *fieldElem = Element::createFromStream(rsc, stream);
             elem->mFields[ct].e.set(fieldElem);
             elem->mFields[ct].offsetBits = offset;
@@ -155,7 +157,8 @@
             for (uint32_t i=0; i < elem->mFieldCount; i++) {
                 if ((ee->mFields[i].e.get() != elem->mFields[i].e.get()) ||
                     (ee->mFields[i].name.length() != elem->mFields[i].name.length()) ||
-                    (ee->mFields[i].name != elem->mFields[i].name)) {
+                    (ee->mFields[i].name != elem->mFields[i].name) ||
+                    (ee->mFields[i].arraySize != elem->mFields[i].arraySize)) {
                     match = false;
                     break;
                 }
@@ -200,7 +203,7 @@
 }
 
 const Element * Element::create(Context *rsc, size_t count, const Element **ein,
-                            const char **nin, const size_t * lengths)
+                            const char **nin, const size_t * lengths, const uint32_t *asin)
 {
     // Look for an existing match.
     for (uint32_t ct=0; ct < rsc->mStateElement.mElements.size(); ct++) {
@@ -210,7 +213,8 @@
             for (uint32_t i=0; i < count; i++) {
                 if ((ee->mFields[i].e.get() != ein[i]) ||
                     (ee->mFields[i].name.length() != lengths[i]) ||
-                    (ee->mFields[i].name != nin[i])) {
+                    (ee->mFields[i].name != nin[i]) ||
+                    (ee->mFields[i].arraySize != asin[i])) {
                     match = false;
                     break;
                 }
@@ -230,6 +234,7 @@
         e->mFields[ct].e.set(ein[ct]);
         e->mFields[ct].name.setTo(nin[ct], lengths[ct]);
         e->mFields[ct].offsetBits = bits;
+        e->mFields[ct].arraySize = asin[ct];
         bits += ein[ct]->getSizeBits();
 
         if (ein[ct]->mHasReference) {
@@ -274,7 +279,11 @@
     const uint8_t *p = static_cast<const uint8_t *>(ptr);
     for (uint32_t i=0; i < mFieldCount; i++) {
         if (mFields[i].e->mHasReference) {
-            mFields[i].e->incRefs(&p[mFields[i].offsetBits >> 3]);
+            p = &p[mFields[i].offsetBits >> 3];
+            for (uint32_t ct=0; ct < mFields[i].arraySize; ct++) {
+                mFields[i].e->incRefs(p);
+                p += mFields[i].e->getSizeBytes();
+            }
         }
     }
 }
@@ -293,7 +302,11 @@
     const uint8_t *p = static_cast<const uint8_t *>(ptr);
     for (uint32_t i=0; i < mFieldCount; i++) {
         if (mFields[i].e->mHasReference) {
-            mFields[i].e->decRefs(&p[mFields[i].offsetBits >> 3]);
+            p = &p[mFields[i].offsetBits >> 3];
+            for (uint32_t ct=0; ct < mFields[i].arraySize; ct++) {
+                mFields[i].e->decRefs(p);
+                p += mFields[i].e->getSizeBytes();
+            }
         }
     }
 }
@@ -331,10 +344,11 @@
                              size_t count,
                              const RsElement * ein,
                              const char ** names,
-                             const size_t * nameLengths)
+                             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);
+    const Element *e = Element::create(rsc, count, (const Element **)ein, names, nameLengths, arraySizes);
     e->incUserRef();
     return (RsElement)e;
 }
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index b5dad7a2..42eef4a 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -66,7 +66,7 @@
     static const Element * create(Context *rsc, RsDataType dt, RsDataKind dk,
                             bool isNorm, uint32_t vecSize);
     static const Element * create(Context *rsc, size_t count, const Element **,
-                            const char **, const size_t * lengths);
+                            const char **, const size_t * lengths, const uint32_t *asin);
 
     void incRefs(const void *) const;
     void decRefs(const void *) const;
@@ -80,6 +80,7 @@
         String8 name;
         ObjectBaseRef<const Element> e;
         uint32_t offsetBits;
+        uint32_t arraySize;
     } ElementField_t;
     ElementField_t *mFields;
     size_t mFieldCount;
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 7661d499..5889bfb 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -488,8 +488,9 @@
     size_t lengths[2];
     lengths[0] = posName.size();
     lengths[1] = texName.size();
+    uint32_t arraySizes[2] = {1, 1};
 
-    const Element *vertexDataElem = Element::create(mRSC, 2, elemArray, nameArray, lengths);
+    const Element *vertexDataElem = Element::create(mRSC, 2, elemArray, nameArray, lengths, arraySizes);
 
     Type *vertexDataType = new Type(mRSC);
     vertexDataType->setDimX(mMaxNumberOfQuads * 4);
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index ec9c569..83321d3 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -62,15 +62,6 @@
         mTextureEnableMask |= 2;
     }
 
-    mUniformCount = 0;
-    mUniformNames[mUniformCount++].setTo("uni_Tex0");
-    mUniformNames[mUniformCount++].setTo("uni_Tex1");
-
-    mConstantColorUniformIndex = -1;
-    //if (!mVaryingColor) {
-        mConstantColorUniformIndex = mUniformCount;
-        mUniformNames[mUniformCount++].setTo("uni_Color");
-    //}
     init(rsc);
 }
 
@@ -89,10 +80,6 @@
 
     LOGE("Custom FP");
 
-    mUniformCount = 2;
-    mUniformNames[0].setTo("uni_Tex0");
-    mUniformNames[1].setTo("uni_Tex1");
-
     mTextureEnableMask = (1 << mTextureCount) -1;
 
     init(rsc);
@@ -156,7 +143,7 @@
             rsc->checkError("ProgramFragment::setupGL2 tex env");
         }
 
-        glUniform1i(sc->fragUniformSlot(ct), ct);
+        glUniform1i(sc->fragUniformSlot(mTextureUniformIndexStart + ct), ct);
         rsc->checkError("ProgramFragment::setupGL2 uniforms");
     }
 
@@ -177,12 +164,12 @@
     mShader.append("uniform vec4 uni_Color;\n");
 
     if (mUserShader.length() > 1) {
+        appendUserConstants();
         for (uint32_t ct=0; ct < mTextureCount; ct++) {
             char buf[256];
             sprintf(buf, "uniform sampler2D uni_Tex%i;\n", ct);
             mShader.append(buf);
         }
-        appendUserConstants();
         mShader.append(mUserShader);
     } else {
         uint32_t mask = mTextureEnableMask;
@@ -275,11 +262,20 @@
 
 void ProgramFragment::init(Context *rsc)
 {
+    mUniformCount = 0;
+    //if (!mVaryingColor) {
+        mConstantColorUniformIndex = mUniformCount;
+        mUniformNames[mUniformCount++].setTo("uni_Color");
+    //}
+
     if (mUserShader.size() > 0) {
         for (uint32_t ct=0; ct < mConstantCount; ct++) {
             initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, "UNI_");
         }
     }
+    mTextureUniformIndexStart = mUniformCount;
+    mUniformNames[mUniformCount++].setTo("uni_Tex0");
+    mUniformNames[mUniformCount++].setTo("uni_Tex1");
 
     createShader();
 }
diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h
index 7c1598e..f08bb25 100644
--- a/libs/rs/rsProgramFragment.h
+++ b/libs/rs/rsProgramFragment.h
@@ -57,6 +57,7 @@
 
     float mConstantColor[4];
     int32_t mConstantColorUniformIndex;
+    int32_t mTextureUniformIndexStart;
 };
 
 class ProgramFragmentState
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index e1628f4..68e3705 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -70,52 +70,7 @@
 
 void ProgramVertex::setupGL(const Context *rsc, ProgramVertexState *state)
 {
-    if ((state->mLast.get() == this) && !mDirty) {
-        return;
-    }
-    state->mLast.set(this);
-
-    const float *f = static_cast<const float *>(mConstants[0]->getPtr());
-
-    glMatrixMode(GL_TEXTURE);
-    if (mTextureMatrixEnable) {
-        glLoadMatrixf(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]);
-    } else {
-        glLoadIdentity();
-    }
-
-    glMatrixMode(GL_MODELVIEW);
-    glLoadIdentity();
-    if (mLightCount) {
-#ifndef ANDROID_RS_BUILD_FOR_HOST // GLES Only
-        int v = 0;
-        glEnable(GL_LIGHTING);
-
-        glLightModelxv(GL_LIGHT_MODEL_TWO_SIDE, &v);
-
-        for (uint32_t ct = 0; ct < mLightCount; ct++) {
-            const Light *l = mLights[ct].get();
-            glEnable(GL_LIGHT0 + ct);
-            l->setupGL(ct);
-        }
-        for (uint32_t ct = mLightCount; ct < MAX_LIGHTS; ct++) {
-            glDisable(GL_LIGHT0 + ct);
-        }
-#endif //ANDROID_RS_BUILD_FOR_HOST
-    } else {
-        glDisable(GL_LIGHTING);
-    }
-
-    if (!f) {
-        LOGE("Must bind constants to vertex program");
-    }
-
-    glMatrixMode(GL_PROJECTION);
-    glLoadMatrixf(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
-    glMatrixMode(GL_MODELVIEW);
-    glLoadMatrixf(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
-
-    mDirty = false;
+    assert(0);
 }
 
 void ProgramVertex::loadShader(Context *rsc) {
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index a140e22..662791d 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -17,7 +17,7 @@
 #include "rsContext.h"
 #include "rsScriptC.h"
 #include "rsMatrix.h"
-#include "../../../external/llvm/libbcc/include/bcc/bcc.h"
+#include "../../compile/libbcc/include/bcc/bcc.h"
 #include "utils/Timers.h"
 
 #include <GLES/gl.h>
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 5a2b6ba..41828dc 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -18,7 +18,6 @@
 #include "rsScriptC.h"
 #include "rsMatrix.h"
 
-#include "acc/acc.h"
 #include "utils/Timers.h"
 
 #include <time.h>
@@ -357,6 +356,11 @@
     return (int)alloc;
 }
 
+void SC_allocationMarkDirty(RsAllocation a)
+{
+    Allocation *alloc = static_cast<Allocation *>(a);
+    alloc->sendDirty();
+}
 
 void SC_ForEach(RsScript vs,
                 RsAllocation vin,
@@ -423,6 +427,8 @@
     { "_Z13rsClearObjectP13rs_allocation", (void *)&SC_clearObject },
     { "_Z10rsIsObject13rs_allocation", (void *)&SC_isObject },
 
+    { "_Z21rsAllocationMarkDirty13rs_allocation", (void *)&SC_allocationMarkDirty },
+
 
     // Debug
     { "_Z7rsDebugPKcf", (void *)&SC_debugF },
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
index a7877cdb..2f8f79a 100644
--- a/libs/rs/rsScriptC_LibGL.cpp
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -18,7 +18,6 @@
 #include "rsScriptC.h"
 #include "rsMatrix.h"
 
-#include "acc/acc.h"
 #include "utils/Timers.h"
 
 #define GL_GLEXT_PROTOTYPES
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index c842ef1..5720b05 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -107,6 +107,10 @@
 extern rs_allocation __attribute__((overloadable))
     rsGetAllocation(const void *);
 
+// Mark the allocation dirty and notify those using it
+extern void __attribute__((overloadable))
+    rsAllocationMarkDirty(rs_allocation);
+
 // Return the dimensions associated with an allocation.
 extern uint32_t __attribute__((overloadable))
     rsAllocationGetDimX(rs_allocation);
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 4973cd8..811edaf 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -129,6 +129,7 @@
         int32_t deviceId,
         int32_t source,
         int32_t action,
+        int32_t flags,
         int32_t edgeFlags,
         int32_t metaState,
         float xOffset,
@@ -142,6 +143,7 @@
         const PointerCoords* pointerCoords) {
     InputEvent::initialize(deviceId, source);
     mAction = action;
+    mFlags = flags;
     mEdgeFlags = edgeFlags;
     mMetaState = metaState;
     mXOffset = xOffset;
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 886c785..df232d4 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -97,6 +97,7 @@
 
 void InputDispatcher::dispatchOnce() {
     nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
+    nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
 
     bool skipPoll = false;
     nsecs_t currentTime;
@@ -146,7 +147,7 @@
             if (mInboundQueue.isEmpty()) {
                 if (mKeyRepeatState.lastKeyEntry) {
                     if (currentTime >= mKeyRepeatState.nextRepeatTime) {
-                        processKeyRepeatLockedInterruptible(currentTime, keyRepeatTimeout);
+                        processKeyRepeatLockedInterruptible(currentTime, keyRepeatDelay);
                         skipPoll = true;
                     } else {
                         if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) {
@@ -335,7 +336,7 @@
 }
 
 void InputDispatcher::processKeyRepeatLockedInterruptible(
-        nsecs_t currentTime, nsecs_t keyRepeatTimeout) {
+        nsecs_t currentTime, nsecs_t keyRepeatDelay) {
     KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
 
     // Search the inbound queue for a key up corresponding to this device.
@@ -352,7 +353,7 @@
         }
     }
 
-    // Synthesize a key repeat after the repeat timeout expired.
+    // Synthesize a key repeat.
     // Reuse the repeated key entry if it is otherwise unreferenced.
     uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
     if (entry->refCount == 1) {
@@ -375,7 +376,7 @@
         entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
     }
 
-    mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout;
+    mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay;
 
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
@@ -392,9 +393,11 @@
 void InputDispatcher::processMotionLockedInterruptible(
         nsecs_t currentTime, MotionEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, "
+    LOGD("processMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+            "action=0x%x, flags=0x%x, "
             "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
-            entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action,
+            entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+            entry->action, entry->flags,
             entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
             entry->downTime);
 
@@ -406,7 +409,7 @@
     }
     for (uint32_t i = 0; i < entry->pointerCount; i++) {
         LOGD("  Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, "
-                "touchMajor=%f, touchMinor=%d, toolMajor=%f, toolMinor=%f, "
+                "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
                 "orientation=%f",
                 i, entry->pointerIds[i],
                 sample->pointerCoords[i].x, sample->pointerCoords[i].y,
@@ -465,7 +468,7 @@
     mCurrentInputTargetsValid = false;
     mLock.unlock();
 
-    mReusableMotionEvent.initialize(entry->deviceId, entry->source, entry->action,
+    mReusableMotionEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
             entry->edgeFlags, entry->metaState,
             0, 0, entry->xPrecision, entry->yPrecision,
             entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds,
@@ -698,12 +701,16 @@
 
         // Apply target flags.
         int32_t action = motionEntry->action;
+        int32_t flags = motionEntry->flags;
         if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) {
             action = AMOTION_EVENT_ACTION_OUTSIDE;
         }
         if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
             action = AMOTION_EVENT_ACTION_CANCEL;
         }
+        if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
+            flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+        }
 
         // If headMotionSample is non-NULL, then it points to the first new sample that we
         // were unable to dispatch during the previous cycle so we resume dispatching from
@@ -726,7 +733,7 @@
 
         // Publish the motion event and the first motion sample.
         status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId,
-                motionEntry->source, action, motionEntry->edgeFlags, motionEntry->metaState,
+                motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState,
                 xOffset, yOffset,
                 motionEntry->xPrecision, motionEntry->yPrecision,
                 motionEntry->downTime, firstMotionSample->eventTime,
@@ -1073,18 +1080,18 @@
 }
 
 void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
-        uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags,
+        uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t edgeFlags,
         uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
         float xPrecision, float yPrecision, nsecs_t downTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("notifyMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
-            "action=0x%x, metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, "
-            "downTime=%lld",
-            eventTime, deviceId, source, policyFlags, action, metaState, edgeFlags,
+            "action=0x%x, flags=0x%x, metaState=0x%x, edgeFlags=0x%x, "
+            "xPrecision=%f, yPrecision=%f, downTime=%lld",
+            eventTime, deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
             xPrecision, yPrecision, downTime);
     for (uint32_t i = 0; i < pointerCount; i++) {
         LOGD("  Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, "
-                "touchMajor=%f, touchMinor=%d, toolMajor=%f, toolMinor=%f, "
+                "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
                 "orientation=%f",
                 i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y,
                 pointerCoords[i].pressure, pointerCoords[i].size,
@@ -1209,7 +1216,7 @@
 
         // Just enqueue a new motion event.
         MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,
-                deviceId, source, policyFlags, action, metaState, edgeFlags,
+                deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
                 xPrecision, yPrecision, downTime,
                 pointerCount, pointerIds, pointerCoords);
 
@@ -1359,7 +1366,7 @@
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY: {
         const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
-        uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events
+        uint32_t policyFlags = POLICY_FLAG_INJECTED;
 
         KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
                 keyEvent->getDeviceId(), keyEvent->getSource(), policyFlags,
@@ -1371,7 +1378,7 @@
 
     case AINPUT_EVENT_TYPE_MOTION: {
         const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
-        uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events
+        uint32_t policyFlags = POLICY_FLAG_INJECTED;
 
         const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
         const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
@@ -1379,7 +1386,8 @@
 
         MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes,
                 motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
-                motionEvent->getAction(), motionEvent->getMetaState(), motionEvent->getEdgeFlags(),
+                motionEvent->getAction(), motionEvent->getFlags(),
+                motionEvent->getMetaState(), motionEvent->getEdgeFlags(),
                 motionEvent->getXPrecision(), motionEvent->getYPrecision(),
                 motionEvent->getDownTime(), uint32_t(pointerCount),
                 motionEvent->getPointerIds(), samplePointerCoords);
@@ -1664,7 +1672,7 @@
 }
 
 InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime,
-        int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action,
+        int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags,
         int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
         nsecs_t downTime, uint32_t pointerCount,
         const int32_t* pointerIds, const PointerCoords* pointerCoords) {
@@ -1676,6 +1684,7 @@
     entry->source = source;
     entry->policyFlags = policyFlags;
     entry->action = action;
+    entry->flags = flags;
     entry->metaState = metaState;
     entry->edgeFlags = edgeFlags;
     entry->xPrecision = xPrecision;
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 8ffb48d..d57b38c 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -1153,7 +1153,7 @@
     int32_t pointerId = 0;
 
     getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, policyFlags,
-            motionEventAction, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+            motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
             1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime);
 }
 
@@ -2324,7 +2324,7 @@
     } // release lock
 
     getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags,
-            motionEventAction, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
+            motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
             pointerCount, pointerIds, pointerCoords,
             xPrecision, yPrecision, mDownTime);
 }
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index cf0f63e..4c402dc 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -318,8 +318,8 @@
         nsecs_t downTime,
         nsecs_t eventTime) {
 #if DEBUG_TRANSPORT_ACTIONS
-    LOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, source=%d, "
-            "action=%d, flags=%d, keyCode=%d, scanCode=%d, metaState=%d, repeatCount=%d,"
+    LOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, source=0x%x, "
+            "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
             "downTime=%lld, eventTime=%lld",
             mChannel->getName().string(),
             deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
@@ -346,6 +346,7 @@
         int32_t deviceId,
         int32_t source,
         int32_t action,
+        int32_t flags,
         int32_t edgeFlags,
         int32_t metaState,
         float xOffset,
@@ -358,12 +359,12 @@
         const int32_t* pointerIds,
         const PointerCoords* pointerCoords) {
 #if DEBUG_TRANSPORT_ACTIONS
-    LOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=%d, "
-            "action=%d, edgeFlags=%d, metaState=%d, xOffset=%f, yOffset=%f, "
+    LOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=0x%x, "
+            "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, xOffset=%f, yOffset=%f, "
             "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
             "pointerCount=%d",
             mChannel->getName().string(),
-            deviceId, source, action, edgeFlags, metaState, xOffset, yOffset,
+            deviceId, source, action, flags, edgeFlags, metaState, xOffset, yOffset,
             xPrecision, yPrecision, downTime, eventTime, pointerCount);
 #endif
 
@@ -379,6 +380,7 @@
     }
 
     mSharedMessage->motion.action = action;
+    mSharedMessage->motion.flags = flags;
     mSharedMessage->motion.edgeFlags = edgeFlags;
     mSharedMessage->motion.metaState = metaState;
     mSharedMessage->motion.xOffset = xOffset;
@@ -664,6 +666,7 @@
             mSharedMessage->deviceId,
             mSharedMessage->source,
             mSharedMessage->motion.action,
+            mSharedMessage->motion.flags,
             mSharedMessage->motion.edgeFlags,
             mSharedMessage->motion.metaState,
             mSharedMessage->motion.xOffset,
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
index 3bc21fa..952b974 100644
--- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -138,6 +138,7 @@
     const int32_t deviceId = 1;
     const int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
     const int32_t action = AMOTION_EVENT_ACTION_MOVE;
+    const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
     const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
     const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
     const float xOffset = -10;
@@ -167,7 +168,7 @@
         }
     }
 
-    status = mPublisher->publishMotionEvent(deviceId, source, action, edgeFlags,
+    status = mPublisher->publishMotionEvent(deviceId, source, action, flags, edgeFlags,
             metaState, xOffset, yOffset, xPrecision, yPrecision,
             downTime, sampleEventTimes[0], pointerCount, pointerIds, samplePointerCoords.array());
     ASSERT_EQ(OK, status)
@@ -213,6 +214,7 @@
     EXPECT_EQ(deviceId, motionEvent->getDeviceId());
     EXPECT_EQ(source, motionEvent->getSource());
     EXPECT_EQ(action, motionEvent->getAction());
+    EXPECT_EQ(flags, motionEvent->getFlags());
     EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
     EXPECT_EQ(metaState, motionEvent->getMetaState());
     EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
@@ -322,12 +324,12 @@
     int32_t pointerIds[pointerCount] = { 0 };
     PointerCoords pointerCoords[pointerCount] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
 
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerIds, pointerCoords);
     ASSERT_EQ(OK, status)
             << "publisher publishMotionEvent should return OK";
 
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerIds, pointerCoords);
     ASSERT_EQ(INVALID_OPERATION, status)
             << "publisher publishMotionEvent should return INVALID_OPERATION because ";
@@ -342,7 +344,7 @@
     int32_t pointerIds[pointerCount];
     PointerCoords pointerCoords[pointerCount];
 
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerIds, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
@@ -356,7 +358,7 @@
     int32_t pointerIds[pointerCount];
     PointerCoords pointerCoords[pointerCount];
 
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerIds, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
@@ -402,7 +404,7 @@
     PointerCoords pointerCoords[pointerCount];
 
     status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_DOWN,
-            0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+            0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
     ASSERT_EQ(OK, status);
 
     status = mPublisher->appendMotionSample(0, pointerCoords);
@@ -419,7 +421,7 @@
     PointerCoords pointerCoords[pointerCount];
 
     status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
-            0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+            0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
     ASSERT_EQ(OK, status);
 
     status = mPublisher->sendDispatchSignal();
@@ -446,7 +448,7 @@
     PointerCoords pointerCoords[pointerCount];
 
     status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
-            0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+            0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
     ASSERT_EQ(OK, status);
 
     for (int count = 1;; count++) {
diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp
index f740fa0..6d3eeee 100644
--- a/libs/utils/PollLoop.cpp
+++ b/libs/utils/PollLoop.cpp
@@ -95,6 +95,7 @@
     RequestedCallback requestedCallback;
     requestedCallback.callback = NULL;
     requestedCallback.looperCallback = NULL;
+    requestedCallback.ident = 0;
     requestedCallback.data = NULL;
     mRequestedCallbacks.insertAt(requestedCallback, 0);
 }
@@ -116,7 +117,7 @@
         mPendingFdsPos++;
         if (outEvents != NULL) *outEvents = pending.events;
         if (outData != NULL) *outData = pending.data;
-        return pending.fd;
+        return pending.ident;
     }
     
     mLock.lock();
@@ -182,6 +183,7 @@
             const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i);
             PendingCallback pending;
             pending.fd = requestedFd.fd;
+            pending.ident = requestedCallback.ident;
             pending.events = revents;
             pending.callback = requestedCallback.callback;
             pending.looperCallback = requestedCallback.looperCallback;
@@ -191,7 +193,7 @@
                 mPendingCallbacks.push(pending);
             } else if (pending.fd != mWakeReadPipeFd) {
                 if (result == POLL_CALLBACK) {
-                    result = pending.fd;
+                    result = pending.ident;
                     if (outEvents != NULL) *outEvents = pending.events;
                     if (outData != NULL) *outData = pending.data;
                 } else {
@@ -268,16 +270,20 @@
     return mAllowNonCallbacks;
 }
 
+void PollLoop::setCallback(int fd, int ident, int events, Callback callback, void* data) {
+    setCallbackCommon(fd, ident, events, callback, NULL, data);
+}
+
 void PollLoop::setCallback(int fd, int events, Callback callback, void* data) {
-    setCallbackCommon(fd, events, callback, NULL, data);
+    setCallbackCommon(fd, POLL_CALLBACK, events, callback, NULL, data);
 }
 
-void PollLoop::setLooperCallback(int fd, int events, ALooper_callbackFunc* callback,
+void PollLoop::setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback,
         void* data) {
-    setCallbackCommon(fd, events, NULL, callback, data);
+    setCallbackCommon(fd, ident, events, NULL, callback, data);
 }
 
-void PollLoop::setCallbackCommon(int fd, int events, Callback callback,
+void PollLoop::setCallbackCommon(int fd, int ident, int events, Callback callback,
         ALooper_callbackFunc* looperCallback, void* data) {
 
 #if DEBUG_CALLBACKS
@@ -305,6 +311,7 @@
     RequestedCallback requestedCallback;
     requestedCallback.callback = callback;
     requestedCallback.looperCallback = looperCallback;
+    requestedCallback.ident = ident;
     requestedCallback.data = data;
 
     ssize_t index = getRequestIndexLocked(fd);
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 4362d14..91e7df3 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -1946,8 +1946,8 @@
         ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass);
         if (offset <= 0) {
             if (offset < 0) {
-                LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %d: 0x%08x\n",
-                        resID, t, e, (int)ip, (int)offset);
+                LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n",
+                        resID, t, e, ip, (int)offset);
                 return offset;
             }
             continue;
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 2b1f490..e5ece8e 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -21,6 +21,7 @@
 #include <utils/Log.h>
 
 #include <cutils/sched_policy.h>
+#include <cutils/properties.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -57,7 +58,7 @@
 // ----------------------------------------------------------------------------
 
 /*
- * Create and run a new thead.
+ * Create and run a new thread.
  *
  * We create it "detached", so it cleans up after itself.
  */
@@ -280,6 +281,22 @@
 #endif
 }
 
+#if defined(HAVE_PTHREADS)
+static pthread_once_t gDoSchedulingGroupOnce = PTHREAD_ONCE_INIT;
+static bool gDoSchedulingGroup = true;
+
+static void checkDoSchedulingGroup(void) {
+    char buf[PROPERTY_VALUE_MAX];
+    int len = property_get("debug.sys.noschedgroups", buf, "");
+    if (len > 0) {
+        int temp;
+        if (sscanf(buf, "%d", &temp) == 1) {
+            gDoSchedulingGroup = temp == 0;
+        }
+    }
+}
+#endif
+
 int androidSetThreadSchedulingGroup(pid_t tid, int grp)
 {
     if (grp > ANDROID_TGROUP_MAX || grp < 0) { 
@@ -287,9 +304,12 @@
     }
 
 #if defined(HAVE_PTHREADS)
-    if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
-                                      SP_BACKGROUND : SP_FOREGROUND)) {
-        return PERMISSION_DENIED;
+    pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup);
+    if (gDoSchedulingGroup) {
+        if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
+                                          SP_BACKGROUND : SP_FOREGROUND)) {
+            return PERMISSION_DENIED;
+        }
     }
 #endif
     
@@ -303,10 +323,13 @@
 #if defined(HAVE_PTHREADS)
     int lasterr = 0;
 
-    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
-        rc = set_sched_policy(tid, SP_BACKGROUND);
-    } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
-        rc = set_sched_policy(tid, SP_FOREGROUND);
+    pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup);
+    if (gDoSchedulingGroup) {
+        if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+            rc = set_sched_policy(tid, SP_BACKGROUND);
+        } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
+            rc = set_sched_policy(tid, SP_FOREGROUND);
+        }
     }
 
     if (rc) {
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index a0e01c6..2d53136 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -636,7 +636,7 @@
         memcpy(buffer, ptr, uncompLen);
     } else {
         if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
-            goto unmap;
+            goto bail;
     }
 
     if (compLen > kSequentialMin)
@@ -644,8 +644,6 @@
 
     result = true;
 
-unmap:
-    file->release();
 bail:
     return result;
 }
@@ -669,7 +667,7 @@
 
     getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
 
-    FileMap* file = createEntryFileMap(entry);
+    const FileMap* file = createEntryFileMap(entry);
     if (file == NULL) {
         goto bail;
     }
@@ -680,23 +678,21 @@
         ssize_t actual = write(fd, ptr, uncompLen);
         if (actual < 0) {
             LOGE("Write failed: %s\n", strerror(errno));
-            goto unmap;
+            goto bail;
         } else if ((size_t) actual != uncompLen) {
             LOGE("Partial write during uncompress (%zd of %zd)\n",
                 (size_t)actual, (size_t)uncompLen);
-            goto unmap;
+            goto bail;
         } else {
             LOGI("+++ successful write\n");
         }
     } else {
         if (!inflateBuffer(fd, ptr, uncompLen, compLen))
-            goto unmap;
+            goto bail;
     }
 
     result = true;
 
-unmap:
-    file->release();
 bail:
     return result;
 }
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index ca364f5..19b0c17 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -299,14 +299,61 @@
             int timeBetweenTimeLapseFrameCaptureMs, int encoderLevel) {
         setParameter(String.format("time-lapse-enable=%d",
                     (enableTimeLapse) ? 1 : 0));
-        setParameter(String.format("use-still-camera-for-time-lapse=%d",
-                    (useStillCameraForTimeLapse) ? 1 : 0));
         setParameter(String.format("time-between-time-lapse-frame-capture=%d",
                     timeBetweenTimeLapseFrameCaptureMs));
         setVideoEncoderLevel(encoderLevel);
     }
 
     /**
+     * Enables time lapse capture and sets its parameters. This method should
+     * be called after setProfile().
+     *
+     * @param timeBetweenTimeLapseFrameCaptureMs time between two captures of time lapse frames.
+     * @hide
+     */
+    public void enableTimeLapse(int timeBetweenTimeLapseFrameCaptureMs) {
+        setParameter(String.format("time-lapse-enable=1"));
+        setParameter(String.format("time-between-time-lapse-frame-capture=%d",
+                    timeBetweenTimeLapseFrameCaptureMs));
+    }
+
+    /**
+     * Sets filename and parameters for auxiliary time lapse video.
+     *
+     * @param fd an open file descriptor to be written into.
+     * @param videoFrameWidth width of the auxiliary video.
+     * @param videoFrameHeight height of the auxiliary video.
+     * @param videoBitRate bit rate of the auxiliary video
+     * @hide
+     * */
+    public void setAuxVideoParameters(FileDescriptor fd,
+            int videoFrameWidth, int videoFrameHeight,
+            int videoBitRate) {
+        setAuxiliaryOutputFile(fd);
+        setParameter(String.format("video-aux-param-width=%d", videoFrameWidth));
+        setParameter(String.format("video-aux-param-height=%d", videoFrameHeight));
+        setParameter(String.format("video-aux-param-encoding-bitrate=%d", videoBitRate));
+    }
+
+    /**
+     * Sets filename and parameters for auxiliary time lapse video.
+     *
+     * @param path The pathname to use for the auxiliary video.
+     * @param videoFrameWidth width of the auxiliary video.
+     * @param videoFrameHeight height of the auxiliary video.
+     * @param videoBitRate bit rate of the auxiliary video
+     * @hide
+     * */
+    public void setAuxVideoParameters(String path,
+            int videoFrameWidth, int videoFrameHeight,
+            int videoBitRate) {
+        setAuxiliaryOutputFile(path);
+        setParameter(String.format("video-aux-param-width=%d", videoFrameWidth));
+        setParameter(String.format("video-aux-param-height=%d", videoFrameHeight));
+        setParameter(String.format("video-aux-param-encoding-bitrate=%d", videoBitRate));
+    }
+
+    /**
      * Sets the format of the output file produced during recording. Call this
      * after setAudioSource()/setVideoSource() but before prepare().
      *
@@ -489,9 +536,8 @@
      * @param fd an open file descriptor to be written into.
      * @throws IllegalStateException if it is called before
      * setOutputFormat() or after prepare()
-     * @hide
      */
-    public void setAuxiliaryOutputFile(FileDescriptor fd) throws IllegalStateException
+    private void setAuxiliaryOutputFile(FileDescriptor fd) throws IllegalStateException
     {
         mPrepareAuxiliaryFile = true;
         mPathAux = null;
@@ -505,9 +551,8 @@
      * @param path The pathname to use.
      * @throws IllegalStateException if it is called before
      * setOutputFormat() or after prepare()
-     * @hide
      */
-    public void setAuxiliaryOutputFile(String path) throws IllegalStateException
+    private void setAuxiliaryOutputFile(String path) throws IllegalStateException
     {
         mPrepareAuxiliaryFile = true;
         mFdAux = null;
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 0c8326e..3487b0f 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -21,11 +21,12 @@
 import android.content.IContentProvider;
 import android.content.Intent;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.provider.MediaStore.Audio;
 import android.provider.MediaStore.MediaColumns;
-import android.provider.MediaStore.MtpObjects;
+import android.provider.MediaStore.Files;
 import android.provider.Mtp;
 import android.util.Log;
 
@@ -44,30 +45,37 @@
     // true if the database has been modified in the current MTP session
     private boolean mDatabaseModified;
 
+    // database for writable MTP device properties
+    private SQLiteDatabase mDevicePropDb;
+    private static final int DEVICE_PROPERTIES_DATABASE_VERSION = 1;
+
     // FIXME - this should be passed in via the constructor
     private final int mStorageID = 0x00010001;
 
     private static final String[] ID_PROJECTION = new String[] {
-            MtpObjects.ObjectColumns._ID, // 0
+            Files.FileColumns._ID, // 0
     };
     private static final String[] PATH_SIZE_PROJECTION = new String[] {
-            MtpObjects.ObjectColumns._ID, // 0
-            MtpObjects.ObjectColumns.DATA, // 1
-            MtpObjects.ObjectColumns.SIZE, // 2
+            Files.FileColumns._ID, // 0
+            Files.FileColumns.DATA, // 1
+            Files.FileColumns.SIZE, // 2
     };
     private static final String[] OBJECT_INFO_PROJECTION = new String[] {
-            MtpObjects.ObjectColumns._ID, // 0
-            MtpObjects.ObjectColumns.DATA, // 1
-            MtpObjects.ObjectColumns.FORMAT, // 2
-            MtpObjects.ObjectColumns.PARENT, // 3
-            MtpObjects.ObjectColumns.SIZE, // 4
-            MtpObjects.ObjectColumns.DATE_MODIFIED, // 5
+            Files.FileColumns._ID, // 0
+            Files.FileColumns.DATA, // 1
+            Files.FileColumns.FORMAT, // 2
+            Files.FileColumns.PARENT, // 3
+            Files.FileColumns.SIZE, // 4
+            Files.FileColumns.DATE_MODIFIED, // 5
     };
-    private static final String ID_WHERE = MtpObjects.ObjectColumns._ID + "=?";
-    private static final String PATH_WHERE = MtpObjects.ObjectColumns.DATA + "=?";
-    private static final String PARENT_WHERE = MtpObjects.ObjectColumns.PARENT + "=?";
+    private static final String ID_WHERE = Files.FileColumns._ID + "=?";
+    private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
+    private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
     private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
-                                            + MtpObjects.ObjectColumns.FORMAT + "=?";
+                                            + Files.FileColumns.FORMAT + "=?";
+
+    private static final String[] DEVICE_PROPERTY_PROJECTION = new String[] { "_id", "value" };
+    private  static final String DEVICE_PROPERTY_WHERE = "code=?";
 
     private final MediaScanner mMediaScanner;
 
@@ -81,8 +89,9 @@
         mContext = context;
         mMediaProvider = context.getContentResolver().acquireProvider("media");
         mVolumeName = volumeName;
-        mObjectsUri = MtpObjects.getContentUri(volumeName);
+        mObjectsUri = Files.getContentUri(volumeName);
         mMediaScanner = new MediaScanner(context);
+        openDevicePropertiesDatabase(context);
     }
 
     @Override
@@ -94,16 +103,32 @@
         }
     }
 
+    private void openDevicePropertiesDatabase(Context context) {
+        mDevicePropDb = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null);
+        int version = mDevicePropDb.getVersion();
+
+        // initialize if necessary
+        if (version != DEVICE_PROPERTIES_DATABASE_VERSION) {
+            mDevicePropDb.execSQL("CREATE TABLE properties (" +
+                    "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
+                    "code INTEGER UNIQUE ON CONFLICT REPLACE," +
+                    "value TEXT" +
+                    ");");
+            mDevicePropDb.execSQL("CREATE INDEX property_index ON properties (code);");
+            mDevicePropDb.setVersion(DEVICE_PROPERTIES_DATABASE_VERSION);
+        }
+    }
+
     private int beginSendObject(String path, int format, int parent,
                          int storage, long size, long modified) {
         mDatabaseModified = true;
         ContentValues values = new ContentValues();
-        values.put(MtpObjects.ObjectColumns.DATA, path);
-        values.put(MtpObjects.ObjectColumns.FORMAT, format);
-        values.put(MtpObjects.ObjectColumns.PARENT, parent);
+        values.put(Files.FileColumns.DATA, path);
+        values.put(Files.FileColumns.FORMAT, format);
+        values.put(Files.FileColumns.PARENT, parent);
         // storage is ignored for now
-        values.put(MtpObjects.ObjectColumns.SIZE, size);
-        values.put(MtpObjects.ObjectColumns.DATE_MODIFIED, modified);
+        values.put(Files.FileColumns.SIZE, size);
+        values.put(Files.FileColumns.DATE_MODIFIED, modified);
 
         try {
             Uri uri = mMediaProvider.insert(mObjectsUri, values);
@@ -219,25 +244,8 @@
 
     private int[] getSupportedPlaybackFormats() {
         return new int[] {
-            MtpConstants.FORMAT_ASSOCIATION,
-            MtpConstants.FORMAT_MP3,
-            MtpConstants.FORMAT_MPEG,
-            MtpConstants.FORMAT_EXIF_JPEG,
-            MtpConstants.FORMAT_TIFF_EP,
-            MtpConstants.FORMAT_GIF,
-            MtpConstants.FORMAT_JFIF,
-            MtpConstants.FORMAT_PNG,
-            MtpConstants.FORMAT_TIFF,
-            MtpConstants.FORMAT_WMA,
-            MtpConstants.FORMAT_OGG,
-            MtpConstants.FORMAT_AAC,
-            MtpConstants.FORMAT_MP4_CONTAINER,
-            MtpConstants.FORMAT_MP2,
-            MtpConstants.FORMAT_3GP_CONTAINER,
-            MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST,
-            MtpConstants.FORMAT_WPL_PLAYLIST,
-            MtpConstants.FORMAT_M3U_PLAYLIST,
-            MtpConstants.FORMAT_PLS_PLAYLIST,
+            // allow transfering arbitrary files
+            MtpConstants.FORMAT_UNDEFINED,
         };
     }
 
@@ -257,8 +265,10 @@
     }
 
     private int[] getSupportedDeviceProperties() {
-        // no device properties yet
-        return null;
+        return new int[] {
+            MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
+            MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
+        };
     }
 
     private int getObjectProperty(int handle, int property,
@@ -272,24 +282,24 @@
                 outIntValue[0] = mStorageID;
                 return MtpConstants.RESPONSE_OK;
             case MtpConstants.PROPERTY_OBJECT_FORMAT:
-                column = MtpObjects.ObjectColumns.FORMAT;
+                column = Files.FileColumns.FORMAT;
                 break;
             case MtpConstants.PROPERTY_PROTECTION_STATUS:
                 // protection status is always 0
                 outIntValue[0] = 0;
                 return MtpConstants.RESPONSE_OK;
             case MtpConstants.PROPERTY_OBJECT_SIZE:
-                column = MtpObjects.ObjectColumns.SIZE;
+                column = Files.FileColumns.SIZE;
                 break;
             case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
-                column = MtpObjects.ObjectColumns.DATA;
+                column = Files.FileColumns.DATA;
                 isString = true;
                 break;
             case MtpConstants.PROPERTY_DATE_MODIFIED:
-                column = MtpObjects.ObjectColumns.DATE_MODIFIED;
+                column = Files.FileColumns.DATE_MODIFIED;
                 break;
             case MtpConstants.PROPERTY_PARENT_OBJECT:
-                column = MtpObjects.ObjectColumns.PARENT;
+                column = Files.FileColumns.PARENT;
                 break;
             case MtpConstants.PROPERTY_PERSISTENT_UID:
                 // PUID is concatenation of storageID and object handle
@@ -306,7 +316,7 @@
         try {
             // for now we are only reading properties from the "objects" table
             c = mMediaProvider.query(mObjectsUri,
-                            new String [] { MtpObjects.ObjectColumns._ID, column },
+                            new String [] { Files.FileColumns._ID, column },
                             ID_WHERE, new String[] { Integer.toString(handle) }, null);
             if (c != null && c.moveToNext()) {
                 if (isString) {
@@ -342,6 +352,68 @@
         return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
     }
 
+    private int setObjectProperty(int handle, int property,
+                            long intValue, String stringValue) {
+        Log.d(TAG, "setObjectProperty: " + property);
+        return MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+    }
+
+    private int getDeviceProperty(int property, long[] outIntValue, char[] outStringValue) {
+        Log.d(TAG, "getDeviceProperty: " + property);
+
+        switch (property) {
+            case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+            case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+                // writable string properties kept in our device property database
+                Cursor c = null;
+                try {
+                    c = mDevicePropDb.query("properties", DEVICE_PROPERTY_PROJECTION,
+                        DEVICE_PROPERTY_WHERE, new String[] {  Integer.toString(property) },
+                        null, null, null);
+
+                    if (c != null && c.moveToNext()) {
+                        String value = c.getString(1);
+                        int length = value.length();
+                        if (length > 255) {
+                            length = 255;
+                        }
+                        value.getChars(0, length, outStringValue, 0);
+                        outStringValue[length] = 0;
+                    } else {
+                        outStringValue[0] = 0;
+                    }
+                    return MtpConstants.RESPONSE_OK;
+                } finally {
+                    if (c != null) {
+                        c.close();
+                    }
+                }
+        }
+
+        return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+    }
+
+    private int setDeviceProperty(int property, long intValue, String stringValue) {
+        Log.d(TAG, "setDeviceProperty: " + property + " : " + stringValue);
+
+        switch (property) {
+            case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+            case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+                // writable string properties kept in our device property database
+                try {
+                    ContentValues values = new ContentValues();
+                    values.put("code", property);
+                    values.put("value", stringValue);
+                    mDevicePropDb.insert("properties", "code", values);
+                    return MtpConstants.RESPONSE_OK;
+                } catch (Exception e) {
+                    return MtpConstants.RESPONSE_GENERAL_ERROR;
+                }
+        }
+
+        return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+    }
+
     private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
                         char[] outName, long[] outSizeModified) {
         Log.d(TAG, "getObjectInfo: " + handle);
@@ -407,7 +479,7 @@
     private int deleteFile(int handle) {
         Log.d(TAG, "deleteFile: " + handle);
         mDatabaseModified = true;
-        Uri uri = MtpObjects.getContentUri(mVolumeName, handle);
+        Uri uri = Files.getContentUri(mVolumeName, handle);
         try {
             if (mMediaProvider.delete(uri, null, null) == 1) {
                 return MtpConstants.RESPONSE_OK;
@@ -422,7 +494,7 @@
 
     private int[] getObjectReferences(int handle) {
         Log.d(TAG, "getObjectReferences for: " + handle);
-        Uri uri = MtpObjects.getReferencesUri(mVolumeName, handle);
+        Uri uri = Files.getReferencesUri(mVolumeName, handle);
         Cursor c = null;
         try {
             c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null);
@@ -450,12 +522,12 @@
 
     private int setObjectReferences(int handle, int[] references) {
         mDatabaseModified = true;
-        Uri uri = MtpObjects.getReferencesUri(mVolumeName, handle);
+        Uri uri = Files.getReferencesUri(mVolumeName, handle);
         int count = references.length;
         ContentValues[] valuesList = new ContentValues[count];
         for (int i = 0; i < count; i++) {
             ContentValues values = new ContentValues();
-            values.put(MtpObjects.ObjectColumns._ID, references[i]);
+            values.put(Files.FileColumns._ID, references[i]);
             valuesList[i] = values;
         }
         try {
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index 4046287..1842cb2 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -30,6 +30,7 @@
 #include "MtpDatabase.h"
 #include "MtpDataPacket.h"
 #include "MtpProperty.h"
+#include "MtpStringBuffer.h"
 #include "MtpUtils.h"
 #include "mtp.h"
 
@@ -47,6 +48,8 @@
 static jmethodID method_getSupportedDeviceProperties;
 static jmethodID method_getObjectProperty;
 static jmethodID method_setObjectProperty;
+static jmethodID method_getDeviceProperty;
+static jmethodID method_setDeviceProperty;
 static jmethodID method_getObjectInfo;
 static jmethodID method_getObjectFilePath;
 static jmethodID method_deleteFile;
@@ -127,7 +130,8 @@
                                             int64_t& fileLength);
     virtual MtpResponseCode         deleteFile(MtpObjectHandle handle);
 
-    bool                            getPropertyInfo(MtpObjectProperty property, int& type);
+    bool                            getObjectPropertyInfo(MtpObjectProperty property, int& type);
+    bool                            getDevicePropertyInfo(MtpDeviceProperty property, int& type);
 
     virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle);
 
@@ -323,14 +327,16 @@
                                             MtpDataPacket& packet) {
     int         type;
 
-    if (!getPropertyInfo(property, type))
-        return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
+    if (!getObjectPropertyInfo(property, type))
+        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
 
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     jint result = env->CallIntMethod(mDatabase, method_getObjectProperty,
                 (jint)handle, (jint)property, mLongBuffer, mStringBuffer);
-    if (result != MTP_RESPONSE_OK)
+    if (result != MTP_RESPONSE_OK) {
+        checkAndClearExceptionFromCallback(env, __FUNCTION__);
         return result;
+    }
 
     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
     jlong longValue = longValues[0];
@@ -384,8 +390,8 @@
             break;
          }
         default:
-            LOGE("unsupported object type\n");
-            return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+            LOGE("unsupported type in getObjectPropertyValue\n");
+            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
     }
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -395,17 +401,179 @@
 MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
                                             MtpObjectProperty property,
                                             MtpDataPacket& packet) {
-    return -1;
+    int         type;
+
+    if (!getObjectPropertyInfo(property, type))
+        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jlong longValue = 0;
+    jstring stringValue = NULL;
+
+    switch (type) {
+        case MTP_TYPE_INT8:
+            longValue = packet.getInt8();
+            break;
+        case MTP_TYPE_UINT8:
+            longValue = packet.getUInt8();
+            break;
+        case MTP_TYPE_INT16:
+            longValue = packet.getInt16();
+            break;
+        case MTP_TYPE_UINT16:
+            longValue = packet.getUInt16();
+            break;
+        case MTP_TYPE_INT32:
+            longValue = packet.getInt32();
+            break;
+        case MTP_TYPE_UINT32:
+            longValue = packet.getUInt32();
+            break;
+        case MTP_TYPE_INT64:
+            longValue = packet.getInt64();
+            break;
+        case MTP_TYPE_UINT64:
+            longValue = packet.getUInt64();
+            break;
+        case MTP_TYPE_STR:
+        {
+            MtpStringBuffer buffer;
+            packet.getString(buffer);
+            stringValue = env->NewStringUTF((const char *)buffer);
+            break;
+         }
+        default:
+            LOGE("unsupported type in getObjectPropertyValue\n");
+            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+    }
+
+    jint result = env->CallIntMethod(mDatabase, method_setObjectProperty,
+                (jint)handle, (jint)property, longValue, stringValue);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
 }
 
 MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
                                             MtpDataPacket& packet) {
-    return -1;
+
+    int         type;
+
+    if (!getDevicePropertyInfo(property, type))
+        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty,
+                (jint)property, mLongBuffer, mStringBuffer);
+    if (result != MTP_RESPONSE_OK) {
+        checkAndClearExceptionFromCallback(env, __FUNCTION__);
+        return result;
+    }
+
+    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+    jlong longValue = longValues[0];
+    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
+
+    switch (type) {
+        case MTP_TYPE_INT8:
+            packet.putInt8(longValue);
+            break;
+        case MTP_TYPE_UINT8:
+            packet.putUInt8(longValue);
+            break;
+        case MTP_TYPE_INT16:
+            packet.putInt16(longValue);
+            break;
+        case MTP_TYPE_UINT16:
+            packet.putUInt16(longValue);
+            break;
+        case MTP_TYPE_INT32:
+            packet.putInt32(longValue);
+            break;
+        case MTP_TYPE_UINT32:
+            packet.putUInt32(longValue);
+            break;
+        case MTP_TYPE_INT64:
+            packet.putInt64(longValue);
+            break;
+        case MTP_TYPE_UINT64:
+            packet.putUInt64(longValue);
+            break;
+        case MTP_TYPE_INT128:
+            packet.putInt128(longValue);
+            break;
+        case MTP_TYPE_UINT128:
+            packet.putInt128(longValue);
+            break;
+        case MTP_TYPE_STR:
+        {
+            jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+            packet.putString(str);
+            env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+            break;
+         }
+        default:
+            LOGE("unsupported type in getDevicePropertyValue\n");
+            return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
+    }
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return MTP_RESPONSE_OK;
 }
 
 MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
                                             MtpDataPacket& packet) {
-    return -1;
+    int         type;
+
+    if (!getDevicePropertyInfo(property, type))
+        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jlong longValue = 0;
+    jstring stringValue = NULL;
+
+    switch (type) {
+        case MTP_TYPE_INT8:
+            longValue = packet.getInt8();
+            break;
+        case MTP_TYPE_UINT8:
+            longValue = packet.getUInt8();
+            break;
+        case MTP_TYPE_INT16:
+            longValue = packet.getInt16();
+            break;
+        case MTP_TYPE_UINT16:
+            longValue = packet.getUInt16();
+            break;
+        case MTP_TYPE_INT32:
+            longValue = packet.getInt32();
+            break;
+        case MTP_TYPE_UINT32:
+            longValue = packet.getUInt32();
+            break;
+        case MTP_TYPE_INT64:
+            longValue = packet.getInt64();
+            break;
+        case MTP_TYPE_UINT64:
+            longValue = packet.getUInt64();
+            break;
+        case MTP_TYPE_STR:
+        {
+            MtpStringBuffer buffer;
+            packet.getString(buffer);
+            stringValue = env->NewStringUTF((const char *)buffer);
+            break;
+         }
+        default:
+            LOGE("unsupported type in setDevicePropertyValue\n");
+            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+    }
+
+    jint result = env->CallIntMethod(mDatabase, method_setDeviceProperty,
+                (jint)property, longValue, stringValue);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
 }
 
 MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
@@ -473,8 +641,10 @@
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,
                 (jint)handle, mStringBuffer, mLongBuffer);
-    if (result != MTP_RESPONSE_OK)
+    if (result != MTP_RESPONSE_OK) {
+        checkAndClearExceptionFromCallback(env, __FUNCTION__);
         return result;
+    }
 
     jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
     filePath.setTo(str, strlen16(str));
@@ -501,7 +671,7 @@
     int                 type;
 };
 
-static const PropertyTableEntry   kPropertyTable[] = {
+static const PropertyTableEntry   kObjectPropertyTable[] = {
     {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32 },
     {   MTP_PROPERTY_STORAGE_ID,        MTP_TYPE_UINT32 },
     {   MTP_PROPERTY_OBJECT_FORMAT,     MTP_TYPE_UINT16 },
@@ -510,9 +680,26 @@
     {   MTP_PROPERTY_DATE_MODIFIED,     MTP_TYPE_STR    },
 };
 
-bool MyMtpDatabase::getPropertyInfo(MtpObjectProperty property, int& type) {
-    int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]);
-    const PropertyTableEntry* entry = kPropertyTable;
+static const PropertyTableEntry   kDevicePropertyTable[] = {
+    {   MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,     MTP_TYPE_STR },
+    {   MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,        MTP_TYPE_STR },
+};
+
+bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
+    int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
+    const PropertyTableEntry* entry = kObjectPropertyTable;
+    for (int i = 0; i < count; i++, entry++) {
+        if (entry->property == property) {
+            type = entry->type;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
+    int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
+    const PropertyTableEntry* entry = kDevicePropertyTable;
     for (int i = 0; i < count; i++, entry++) {
         if (entry->property == property) {
             type = entry->type;
@@ -587,7 +774,16 @@
 }
 
 MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
-    return NULL;
+    MtpProperty* result = NULL;
+    switch (property) {
+        case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+        case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+            // writeable string properties
+            result = new MtpProperty(property, MTP_TYPE_STR, true);
+            break;
+    }
+
+    return result;
 }
 
 void MyMtpDatabase::sessionStarted() {
@@ -695,6 +891,21 @@
         LOGE("Can't find getObjectProperty");
         return -1;
     }
+    method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I");
+    if (method_setObjectProperty == NULL) {
+        LOGE("Can't find setObjectProperty");
+        return -1;
+    }
+    method_getDeviceProperty = env->GetMethodID(clazz, "getDeviceProperty", "(I[J[C)I");
+    if (method_getDeviceProperty == NULL) {
+        LOGE("Can't find getDeviceProperty");
+        return -1;
+    }
+    method_setDeviceProperty = env->GetMethodID(clazz, "setDeviceProperty", "(IJLjava/lang/String;)I");
+    if (method_setDeviceProperty == NULL) {
+        LOGE("Can't find setDeviceProperty");
+        return -1;
+    }
     method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
     if (method_getObjectInfo == NULL) {
         LOGE("Can't find getObjectInfo");
diff --git a/media/jni/android_media_MtpServer.cpp b/media/jni/android_media_MtpServer.cpp
index 3da3165..4567965 100644
--- a/media/jni/android_media_MtpServer.cpp
+++ b/media/jni/android_media_MtpServer.cpp
@@ -25,7 +25,9 @@
 #include <sys/ioctl.h>
 #include <utils/threads.h>
 
+#ifdef HAVE_ANDROID_OS
 #include <linux/usb/f_mtp.h>
+#endif
 
 #include "jni.h"
 #include "JNIHelp.h"
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.c
index 6a8b39b..9a40f68 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.c
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.c
@@ -526,7 +526,7 @@
         (pPrivate->NewParams.OperatingMode == LVM_MODE_ON))
     {
         pPrivate->BypassMixer.Target2 = ((LVM_INT32)(pPrivate->NewParams.Level * 32767)/100)<<16;
-        pPrivate->BypassMixer.Target1 = LVREV_HEADROOM << 16;
+        pPrivate->BypassMixer.Target1 = 0x00000000;
         if ((pPrivate->NewParams.Level == 0) && (pPrivate->bFirstControl == LVM_FALSE))
         {
             pPrivate->BypassMixer.CallbackSet2 = LVM_TRUE;
@@ -542,7 +542,7 @@
         if(pPrivate->NewParams.OperatingMode == LVM_MODE_ON)
         {
             pPrivate->BypassMixer.Target2 = ((LVM_INT32)(pPrivate->NewParams.Level * 32767)/100)<<16;
-            pPrivate->BypassMixer.Target1 = LVREV_HEADROOM << 16;
+            pPrivate->BypassMixer.Target1 = 0x00000000;
 
             pPrivate->BypassMixer.CallbackSet2 = LVM_FALSE;
             OperatingMode                      = LVM_MODE_ON;
@@ -558,7 +558,7 @@
         else if (pPrivate->bFirstControl == LVM_FALSE)
         {
             pPrivate->BypassMixer.Target2 = 0x00000000;
-            pPrivate->BypassMixer.Target1 = 0x7FFFFFFF;
+            pPrivate->BypassMixer.Target1 = 0x00000000;
             pPrivate->BypassMixer.CallbackSet2 = LVM_TRUE;
             pPrivate->GainMixer.Target    = 0x03FFFFFF;
             OperatingMode = LVM_MODE_ON;
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.c
index c2b266a..ffa5138 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.c
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.c
@@ -238,8 +238,8 @@
     pLVREV_Private->BypassMixer.pGeneralPurpose1    = LVM_NULL;
     pLVREV_Private->BypassMixer.pCallBack1          = LVM_NULL;
     pLVREV_Private->BypassMixer.CallbackSet1        = LVM_FALSE;
-    pLVREV_Private->BypassMixer.Current1            = 0x7fffffff;
-    pLVREV_Private->BypassMixer.Target1             = 0x7fffffff;
+    pLVREV_Private->BypassMixer.Current1            = 0x00000000;
+    pLVREV_Private->BypassMixer.Target1             = 0x00000000;
 
     pLVREV_Private->RoomSizeInms                    = 100;  // 100 msec
 
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c
index a3ba42b..ce6d410 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c
@@ -202,7 +202,9 @@
         /* Set the reverb delay timeout */
         if(pInstance->bInOperatingModeTransition != LVM_TRUE){
             pInstance->bTimerDone = LVM_FALSE;
-            pInstance->TimerParams.TimeInMs = (LVM_INT16)(((pInstance->Reverberation.DelaySize << 2)/pInstance->TimerParams.SamplingRate) + 1);
+            pInstance->TimerParams.TimeInMs =
+            (LVM_INT16)(((pInstance->Reverberation.DelaySize << 2)
+            /pInstance->TimerParams.SamplingRate) + 1);
             LVM_Timer_Init ( &pInstance->TimerInstance,
                              &pInstance->TimerParams);
         }
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 7297811..e86ed99 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -275,7 +275,6 @@
            pContext->pBundledContext->PcmInPtr = NULL;
            return -EINVAL;
         }
-
         #endif
 
         /* Saved strength is used to return the exact strength that was used in the set to the get
@@ -565,9 +564,6 @@
     params.SpeakerType            = LVM_HEADPHONES;
 
     pContext->pBundledContext->SampleRate = LVM_FS_44100;
-    pContext->pBundledContext->SamplesToExitCountEq   = 44100*2*2; // 2 secs Stereo
-    pContext->pBundledContext->SamplesToExitCountBb   = 44100*2*2; // 2 secs Stereo
-    pContext->pBundledContext->SamplesToExitCountVirt = 44100*2*2; // 2 secs Stereo
 
     /* Concert Sound parameters */
     params.VirtualizerOperatingMode   = LVM_MODE_OFF;
@@ -2431,7 +2427,7 @@
         if(pContext->pBundledContext->SamplesToExitCountBb > 0){
             status2Sec = -ENODATA;
             pContext->pBundledContext->SamplesToExitCountBb -= outBuffer->frameCount * 2; // STEREO
-            //LOGV("\tEffect_process: Waiting for 2 secs to turn off BASS_BOOST, %d samples left",
+            //LOGV("\tEffect_process: Waiting to turn off BASS_BOOST, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountBb);
         } else {
         status = -ENODATA;
@@ -2897,11 +2893,11 @@
             pContext->pBundledContext->NumberEffectsEnabled++;
             android::LvmEffect_enable(pContext);
             pContext->pBundledContext->SamplesToExitCountEq =
-                 (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*1); // 0.1 secs Stereo
+                 (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); // 0.1 secs Stereo
             pContext->pBundledContext->SamplesToExitCountBb =
-                 (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*6); // 2 secs Stereo
+                 (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); // 0.1 secs Stereo
             pContext->pBundledContext->SamplesToExitCountVirt =
-                 (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*1); // 2 secs Stereo
+                 (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); // 0.1 secs Stereo
             LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE Samples to Exit = %d",
                 pContext->pBundledContext->SamplesToExitCountBb);
             //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE NumberEffectsEnabled = %d",
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 10c3e92..45ef416 100755
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -152,6 +152,7 @@
     bool                            preset;
     uint16_t                        curPreset;
     uint16_t                        nextPreset;
+    int                             SamplesToExitCount;
 };
 
 #define REVERB_DEFAULT_PRESET REVERB_PRESET_MEDIUMROOM
@@ -212,6 +213,7 @@
         desc = gDescriptors[i];
         if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t))
                 == 0) {
+            LOGV("\tEffectCreate - UUID matched Reverb type %d, UUID = %x", i, desc->uuid.timeLow);
             break;
         }
     }
@@ -228,6 +230,9 @@
     pContext->auxiliary = false;
     if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY){
         pContext->auxiliary = true;
+        LOGV("\tEffectCreate - AUX");
+    }else{
+        LOGV("\tEffectCreate - INS");
     }
 
     pContext->preset = false;
@@ -236,6 +241,9 @@
         // force reloading preset at first call to process()
         pContext->curPreset = REVERB_PRESET_LAST + 1;
         pContext->nextPreset = REVERB_DEFAULT_PRESET;
+        LOGV("\tEffectCreate - PRESET");
+    }else{
+        LOGV("\tEffectCreate - ENVIRONMENTAL");
     }
 
     LOGV("\tEffectCreate - Calling Reverb_init");
@@ -389,22 +397,19 @@
              int           frameCount,
              ReverbContext *pContext){
 
-    LVM_INT16               samplesPerFrame = 0;
+    LVM_INT16               samplesPerFrame = 1;
     LVREV_ReturnStatus_en   LvmStatus = LVREV_SUCCESS;              /* Function call status */
     LVM_INT16 *OutFrames16;
 
 
     // Check that the input is either mono or stereo
-    if(pContext->config.inputCfg.channels == CHANNEL_STEREO){
+    if (pContext->config.inputCfg.channels == CHANNEL_STEREO) {
         samplesPerFrame = 2;
-    } else if (pContext->config.inputCfg.channels == CHANNEL_MONO){
-        samplesPerFrame = 1;
-    } else {
+    } else if (pContext->config.inputCfg.channels != CHANNEL_MONO) {
         LOGV("\tLVREV_ERROR : process invalid PCM format");
         return -EINVAL;
     }
 
-
     OutFrames16 = (LVM_INT16 *)pContext->OutFrames32;
 
     // Check for NULL pointers
@@ -437,43 +442,49 @@
     //pContext->config.outputCfg.channels, CHANNEL_STEREO);
 
     if (pContext->preset && pContext->curPreset == REVERB_PRESET_NONE) {
-        memset(pContext->OutFrames32, 0, frameCount * sizeof(LVM_INT32) * 2);
+        memset(pContext->OutFrames32, 0, frameCount * sizeof(LVM_INT32) * 2); //always stereo here
     } else {
-    /* Process the samples */
-    LvmStatus = LVREV_Process(pContext->hInstance,      /* Instance handle */
-                              pContext->InFrames32,     /* Input buffer */
-                              pContext->OutFrames32,    /* Output buffer */
-                              frameCount);              /* Number of samples to read */
-    }
-
-    if (!pContext->auxiliary) {
-        for (int i=0; i<frameCount*2; i++){
-            pContext->OutFrames32[i] += pContext->InFrames32[i];
+        if(pContext->bEnabled == LVM_FALSE && pContext->SamplesToExitCount > 0) {
+            memset(pContext->InFrames32,
+                   0,
+                   frameCount * sizeof(LVM_INT32) * 2); //always stereo here
         }
+
+        /* Process the samples */
+        LvmStatus = LVREV_Process(pContext->hInstance,      /* Instance handle */
+                                  pContext->InFrames32,     /* Input buffer */
+                                  pContext->OutFrames32,    /* Output buffer */
+                                  frameCount);              /* Number of samples to read */
     }
 
     LVM_ERROR_CHECK(LvmStatus, "LVREV_Process", "process")
     if(LvmStatus != LVREV_SUCCESS) return -EINVAL;
 
     // Convert to 16 bits
-    for(int i=0; i<frameCount*2; i++){  // Always stereo
-        OutFrames16[i] = clamp16(pContext->OutFrames32[i]>>8);
+    if (pContext->auxiliary) {
+        for (int i=0; i < frameCount*2; i++) { //always stereo here
+            OutFrames16[i] = clamp16(pContext->OutFrames32[i]>>8);
+        }
+    } else {
+        for (int i=0; i < frameCount*2; i++) { //always stereo here
+            OutFrames16[i] = clamp16((pContext->OutFrames32[i]>>8) + (LVM_INT32)pIn[i]);
+        }
     }
 
     #ifdef LVM_PCM
-    fwrite(OutFrames16, frameCount*sizeof(LVM_INT16)*samplesPerFrame, 1, pContext->PcmOutPtr);
+    fwrite(OutFrames16, frameCount*sizeof(LVM_INT16)*2, 1, pContext->PcmOutPtr);
     fflush(pContext->PcmOutPtr);
     #endif
 
     // Accumulate if required
     if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
         //LOGV("\tBuffer access is ACCUMULATE");
-        for (int i=0; i<frameCount*2; i++){
+        for (int i=0; i<frameCount*2; i++){ //always stereo here
             pOut[i] = clamp16((int32_t)pOut[i] + (int32_t)OutFrames16[i]);
         }
     }else{
         //LOGV("\tBuffer access is WRITE");
-        memcpy(pOut, OutFrames16, frameCount*sizeof(LVM_INT16)*2); // 2 is for stereo output
+        memcpy(pOut, OutFrames16, frameCount*sizeof(LVM_INT16)*2);
     }
 
     return 0;
@@ -560,6 +571,7 @@
     //LOGV("\tReverb_configure calling memcpy");
     memcpy(&pContext->config, pConfig, sizeof(effect_config_t));
 
+
     switch (pConfig->inputCfg.samplingRate) {
     case 8000:
         SampleRate = LVM_FS_8000;
@@ -741,6 +753,8 @@
     params.Damping        = 21;
     params.RoomSize       = 100;
 
+    pContext->SamplesToExitCount = (params.T60 * pContext->config.inputCfg.samplingRate)/1000;
+
     /* Saved strength is used to return the exact strength that was used in the set to the get
      * because we map the original strength range of 0:1000 to 1:15, and this will avoid
      * quantisation like effect when returning
@@ -1082,7 +1096,8 @@
 
     //LOGV("\tReverbGetRoomLevel, Level = %d, pContext->SavedRoomLevel = %d, "
     //     "pContext->SavedReverbLevel = %d, CombinedLevel = %d, level = %d",
-    //ActiveParams.Level, pContext->SavedRoomLevel, pContext->SavedReverbLevel, CombinedLevel, level);
+    //     ActiveParams.Level, pContext->SavedRoomLevel,
+    //     pContext->SavedReverbLevel, CombinedLevel, level);
 
     if(ActiveParams.Level != level){
         LOGV("\tLVM_ERROR : (ignore at start up) ReverbGetRoomLevel() has wrong level -> %d %d\n",
@@ -1129,6 +1144,8 @@
     LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "ReverbSetDecayTime")
     //LOGV("\tReverbSetDecayTime() just Set -> %d\n", ActiveParams.T60);
 
+    pContext->SamplesToExitCount = (ActiveParams.T60 * pContext->config.inputCfg.samplingRate)/1000;
+    //LOGV("\tReverbSetDecayTime() just Set SamplesToExitCount-> %d\n",pContext->SamplesToExitCount);
     pContext->SavedDecayTime = time;
     //LOGV("\tReverbSetDecayTime end");
     return;
@@ -1769,8 +1786,12 @@
         return -EINVAL;
     }
     if (pContext->bEnabled == LVM_FALSE){
-        LOGV("\tReverb_process() ERROR Effect is not enabled");
-        return -ENODATA;
+        if( pContext->SamplesToExitCount > 0){
+            pContext->SamplesToExitCount -= outBuffer->frameCount;
+        }else{
+            LOGV("\tReverb_process() ERROR Effect is not enabled %d", pContext->SamplesToExitCount);
+            return -ENODATA;
+        }
     }
     //LOGV("\tReverb_process() Calling process with %d frames", outBuffer->frameCount);
     /* Process all the available frames, block processing is handled internalLY by the LVM bundle */
@@ -1791,6 +1812,9 @@
                               void                *pReplyData){
     android::ReverbContext * pContext = (android::ReverbContext *) self;
     int retsize;
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+
 
     if (pContext == NULL){
         LOGV("\tLVM_ERROR : Reverb_command ERROR pContext == NULL");
@@ -1917,6 +1941,12 @@
              }
             *(int *)pReplyData = 0;
             pContext->bEnabled = LVM_TRUE;
+            /* Get the current settings */
+            LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+            LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "EFFECT_CMD_ENABLE")
+            pContext->SamplesToExitCount =
+                    (ActiveParams.T60 * pContext->config.inputCfg.samplingRate)/1000;
+            //LOGV("\tEFFECT_CMD_ENABLE SamplesToExitCount = %d", pContext->SamplesToExitCount);
             break;
         case EFFECT_CMD_DISABLE:
             //LOGV("\tReverb_command cmdCode Case: "
@@ -1939,8 +1969,8 @@
         case EFFECT_CMD_SET_DEVICE:
         case EFFECT_CMD_SET_VOLUME:
         case EFFECT_CMD_SET_AUDIO_MODE:
-            //LOGV("\tReverb_command cmdCode Case: "
-            //        "EFFECT_CMD_SET_DEVICE/EFFECT_CMD_SET_VOLUME/EFFECT_CMD_SET_AUDIO_MODE start");
+        //LOGV("\tReverb_command cmdCode Case: "
+        //        "EFFECT_CMD_SET_DEVICE/EFFECT_CMD_SET_VOLUME/EFFECT_CMD_SET_AUDIO_MODE start");
             break;
 
         default:
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index f3804b8..40801a2 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -21,7 +21,9 @@
     SET_PARAMETER,
     GET_CONFIG,
     SET_CONFIG,
+    ENABLE_GRAPHIC_BUFFERS,
     USE_BUFFER,
+    USE_GRAPHIC_BUFFER,
     ALLOC_BUFFER,
     ALLOC_BUFFER_WITH_BACKUP,
     FREE_BUFFER,
@@ -216,6 +218,19 @@
         return reply.readInt32();
     }
 
+    virtual status_t enableGraphicBuffers(
+            node_id node, OMX_U32 port_index, OMX_BOOL enable) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeIntPtr((intptr_t)node);
+        data.writeInt32(port_index);
+        data.writeInt32((uint32_t)enable);
+        remote()->transact(ENABLE_GRAPHIC_BUFFERS, data, &reply);
+
+        status_t err = reply.readInt32();
+        return err;
+    }
+
     virtual status_t useBuffer(
             node_id node, OMX_U32 port_index, const sp<IMemory> &params,
             buffer_id *buffer) {
@@ -238,6 +253,29 @@
         return err;
     }
 
+
+    virtual status_t useGraphicBuffer(
+            node_id node, OMX_U32 port_index,
+            const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeIntPtr((intptr_t)node);
+        data.writeInt32(port_index);
+        data.write(*graphicBuffer);
+        remote()->transact(USE_GRAPHIC_BUFFER, data, &reply);
+
+        status_t err = reply.readInt32();
+        if (err != OK) {
+            *buffer = 0;
+
+            return err;
+        }
+
+        *buffer = (void*)reply.readIntPtr();
+
+        return err;
+    }
+
     virtual status_t allocateBuffer(
             node_id node, OMX_U32 port_index, size_t size,
             buffer_id *buffer, void **buffer_data) {
@@ -541,6 +579,20 @@
             return NO_ERROR;
         }
 
+        case ENABLE_GRAPHIC_BUFFERS:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = (void*)data.readIntPtr();
+            OMX_U32 port_index = data.readInt32();
+            OMX_BOOL enable = (OMX_BOOL)data.readInt32();
+
+            status_t err = enableGraphicBuffers(node, port_index, enable);
+            reply->writeInt32(err);
+
+            return NO_ERROR;
+        }
+
         case USE_BUFFER:
         {
             CHECK_INTERFACE(IOMX, data, reply);
@@ -561,6 +613,27 @@
             return NO_ERROR;
         }
 
+        case USE_GRAPHIC_BUFFER:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = (void*)data.readIntPtr();
+            OMX_U32 port_index = data.readInt32();
+            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
+            data.read(*graphicBuffer);
+
+            buffer_id buffer;
+            status_t err = useGraphicBuffer(
+                    node, port_index, graphicBuffer, &buffer);
+            reply->writeInt32(err);
+
+            if (err == OK) {
+                reply->writeIntPtr((intptr_t)buffer);
+            }
+
+            return NO_ERROR;
+        }
+
         case ALLOC_BUFFER:
         {
             CHECK_INTERFACE(IOMX, data, reply);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index f1b8334..77a9cca 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -507,19 +507,6 @@
     return OK;
 }
 
-status_t StagefrightRecorder::setParamUseStillCameraForTimeLapse(int32_t useStillCamera) {
-    LOGV("setParamUseStillCameraForTimeLapse: %d", useStillCamera);
-
-    if(useStillCamera == 0) {
-        mUseStillCameraForTimeLapse= false;
-    } else if (useStillCamera == 1) {
-        mUseStillCameraForTimeLapse= true;
-    } else {
-        return BAD_VALUE;
-    }
-    return OK;
-}
-
 status_t StagefrightRecorder::setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs) {
     LOGV("setParamTimeBetweenTimeLapseFrameCapture: %lld us", timeUs);
 
@@ -657,11 +644,6 @@
         if (safe_strtoi32(value.string(), &timeLapseEnable)) {
             return setParamTimeLapseEnable(timeLapseEnable);
         }
-    } else if (key == "use-still-camera-for-time-lapse") {
-        int32_t useStillCamera;
-        if (safe_strtoi32(value.string(), &useStillCamera)) {
-            return setParamUseStillCameraForTimeLapse(useStillCamera);
-        }
     } else if (key == "time-between-time-lapse-frame-capture") {
         int64_t timeBetweenTimeLapseFrameCaptureMs;
         if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureMs)) {
@@ -1008,10 +990,10 @@
     // Set the actual video recording frame size
     CameraParameters params(mCamera->getParameters());
 
-    // dont change the preview size when using still camera for time lapse
+    // dont change the preview size because time lapse may be using still camera
     // as mVideoWidth, mVideoHeight may correspond to HD resolution not
     // supported by the video camera.
-    if (!(mCaptureTimeLapse && mUseStillCameraForTimeLapse)) {
+    if (!mCaptureTimeLapse) {
         params.setPreviewSize(mVideoWidth, mVideoHeight);
     }
 
@@ -1027,7 +1009,7 @@
     // Check on video frame size
     int frameWidth = 0, frameHeight = 0;
     newCameraParams.getPreviewSize(&frameWidth, &frameHeight);
-    if (!(mCaptureTimeLapse && mUseStillCameraForTimeLapse) &&
+    if (!mCaptureTimeLapse &&
         (frameWidth  < 0 || frameWidth  != mVideoWidth ||
         frameHeight < 0 || frameHeight != mVideoHeight)) {
         LOGE("Failed to set the video frame size to %dx%d",
@@ -1072,7 +1054,7 @@
     if (err != OK) return err;
 
     *cameraSource = (mCaptureTimeLapse) ?
-        CameraSourceTimeLapse::CreateFromCamera(mCamera, mUseStillCameraForTimeLapse,
+        CameraSourceTimeLapse::CreateFromCamera(mCamera,
                 mTimeBetweenTimeLapseFrameCaptureUs, mVideoWidth, mVideoHeight, mFrameRate):
         CameraSource::CreateFromCamera(mCamera);
     CHECK(*cameraSource != NULL);
@@ -1418,7 +1400,6 @@
     mMaxFileSizeBytes = 0;
     mTrackEveryTimeDurationUs = 0;
     mCaptureTimeLapse = false;
-    mUseStillCameraForTimeLapse = true;
     mTimeBetweenTimeLapseFrameCaptureUs = -1;
     mCaptureAuxVideo = false;
     mCameraSourceSplitter = NULL;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index d50a393..f1dc9e6 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -98,7 +98,6 @@
     int64_t mTrackEveryTimeDurationUs;
 
     bool mCaptureTimeLapse;
-    bool mUseStillCameraForTimeLapse;
     int64_t mTimeBetweenTimeLapseFrameCaptureUs;
     bool mCaptureAuxVideo;
     sp<MediaSourceSplitter> mCameraSourceSplitter;
@@ -138,7 +137,6 @@
     status_t setParamAudioSamplingRate(int32_t sampleRate);
     status_t setParamAudioTimeScale(int32_t timeScale);
     status_t setParamTimeLapseEnable(int32_t timeLapseEnable);
-    status_t setParamUseStillCameraForTimeLapse(int32_t useStillCamera);
     status_t setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs);
     status_t setParamAuxVideoHeight(int32_t height);
     status_t setParamAuxVideoWidth(int32_t width);
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 4c729e4..c2f79e8 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -72,6 +72,10 @@
         return UNKNOWN_ERROR;
     }
 
+    if (mInitCheck != OK) {
+        return NO_INIT;
+    }
+
     char value[PROPERTY_VALUE_MAX];
     if (property_get("media.stagefright.record-stats", value, NULL)
         && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
@@ -80,6 +84,7 @@
 
     mTrackMaxAmplitude = false;
     mMaxAmplitude = 0;
+    mInitialReadTimeUs = 0;
     mStartTimeUs = 0;
     int64_t startTimeUs;
     if (params && params->findInt64(kKeyTime, &startTimeUs)) {
@@ -102,6 +107,10 @@
         return UNKNOWN_ERROR;
     }
 
+    if (mInitCheck != OK) {
+        return NO_INIT;
+    }
+
     mRecord->stop();
 
     delete mGroup;
@@ -118,6 +127,10 @@
 }
 
 sp<MetaData> AudioSource::getFormat() {
+    if (mInitCheck != OK) {
+        return 0;
+    }
+
     sp<MetaData> meta = new MetaData;
     meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
     meta->setInt32(kKeySampleRate, mRecord->getSampleRate());
@@ -193,6 +206,12 @@
 
 status_t AudioSource::read(
         MediaBuffer **out, const ReadOptions *options) {
+
+    if (mInitCheck != OK) {
+        return NO_INIT;
+    }
+
+    int64_t readTimeUs = systemTime() / 1000;
     *out = NULL;
 
     MediaBuffer *buffer;
@@ -206,9 +225,10 @@
 
 
         if (numFramesRecorded == 0 && mPrevSampleTimeUs == 0) {
+            mInitialReadTimeUs = readTimeUs;
             // Initial delay
             if (mStartTimeUs > 0) {
-                mStartTimeUs = systemTime() / 1000 - mStartTimeUs;
+                mStartTimeUs = readTimeUs - mStartTimeUs;
             } else {
                 // Assume latency is constant.
                 mStartTimeUs += mRecord->latency() * 1000;
@@ -254,7 +274,10 @@
             }
             memset(buffer->data(), 0, numLostBytes);
             buffer->set_range(0, numLostBytes);
-            buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
+            if (numFramesRecorded == 0) {
+                buffer->meta_data()->setInt64(kKeyTime, mStartTimeUs);
+            }
+            buffer->meta_data()->setInt64(kKeyDriftTime, readTimeUs - mInitialReadTimeUs);
             mPrevSampleTimeUs = timestampUs;
             *out = buffer;
             return OK;
@@ -292,12 +315,11 @@
             trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
         }
 
-        buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
-        CHECK(timestampUs > mPrevSampleTimeUs);
-        if (mTotalLostFrames == 0) {
-            CHECK_EQ(mPrevSampleTimeUs,
-                mStartTimeUs + (1000000LL * numFramesRecorded) / sampleRate);
+        if (numFramesRecorded == 0) {
+            buffer->meta_data()->setInt64(kKeyTime, mStartTimeUs);
         }
+        buffer->meta_data()->setInt64(kKeyDriftTime, readTimeUs - mInitialReadTimeUs);
+        CHECK(timestampUs > mPrevSampleTimeUs);
         mPrevSampleTimeUs = timestampUs;
         LOGV("initial delay: %lld, sample rate: %d, timestamp: %lld",
                 mStartTimeUs, sampleRate, timestampUs);
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 183874c..fd5f30b 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -50,6 +50,9 @@
 
 namespace android {
 
+static int64_t kLowWaterMarkUs = 2000000ll;  // 2secs
+static int64_t kHighWaterMarkUs = 10000000ll;  // 10secs
+
 struct AwesomeEvent : public TimedEventQueue::Event {
     AwesomeEvent(
             AwesomePlayer *player,
@@ -330,6 +333,13 @@
         } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
             setAudioSource(extractor->getTrack(i));
             haveAudio = true;
+
+            sp<MetaData> fileMeta = extractor->getMetaData();
+            int32_t loop;
+            if (fileMeta != NULL
+                    && fileMeta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
+                mFlags |= AUTO_LOOPING;
+            }
         }
 
         if (haveAudio && haveVideo) {
@@ -453,6 +463,25 @@
     }
 }
 
+// Returns true iff cached duration is available/applicable.
+bool AwesomePlayer::getCachedDuration_l(int64_t *durationUs, bool *eos) {
+    off_t totalSize;
+
+    if (mRTSPController != NULL) {
+        *durationUs = mRTSPController->getQueueDurationUs(eos);
+        return true;
+    } else if (mCachedSource != NULL && mDurationUs >= 0
+            && mCachedSource->getSize(&totalSize) == OK) {
+        int64_t bitrate = totalSize * 8000000ll / mDurationUs;  // in bits/sec
+
+        size_t cachedDataRemaining = mCachedSource->approxDataRemaining(eos);
+        *durationUs = cachedDataRemaining * 8000000ll / bitrate;
+        return true;
+    }
+
+    return false;
+}
+
 void AwesomePlayer::onBufferingUpdate() {
     Mutex::Autolock autoLock(mLock);
     if (!mBufferingEventPending) {
@@ -460,49 +489,80 @@
     }
     mBufferingEventPending = false;
 
-    if (mCachedSource == NULL) {
-        return;
-    }
+    if (mCachedSource != NULL) {
+        bool eos;
+        size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
 
-    bool eos;
-    size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
+        if (eos) {
+            notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+        } else {
+            off_t size;
+            if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
+                int64_t bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
 
-    size_t lowWatermark = 400000;
-    size_t highWatermark = 1000000;
+                size_t cachedSize = mCachedSource->cachedSize();
+                int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
 
-    if (eos) {
-        notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
-    } else {
-        off_t size;
-        if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
-            int64_t bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
+                int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
+                if (percentage > 100) {
+                    percentage = 100;
+                }
 
-            size_t cachedSize = mCachedSource->cachedSize();
-            int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
+                notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
+            } else {
+                // We don't know the bitrate of the stream, use absolute size
+                // limits to maintain the cache.
 
-            int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
-            if (percentage > 100) {
-                percentage = 100;
+                const size_t kLowWaterMarkBytes = 400000;
+                const size_t kHighWaterMarkBytes = 1000000;
+
+                if ((mFlags & PLAYING) && !eos
+                        && (cachedDataRemaining < kLowWaterMarkBytes)) {
+                    LOGI("cache is running low (< %d) , pausing.",
+                         kLowWaterMarkBytes);
+                    mFlags |= CACHE_UNDERRUN;
+                    pause_l();
+                    notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
+                } else if (eos || cachedDataRemaining > kHighWaterMarkBytes) {
+                    if (mFlags & CACHE_UNDERRUN) {
+                        LOGI("cache has filled up (> %d), resuming.",
+                             kHighWaterMarkBytes);
+                        mFlags &= ~CACHE_UNDERRUN;
+                        play_l();
+                        notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+                    } else if (mFlags & PREPARING) {
+                        LOGV("cache has filled up (> %d), prepare is done",
+                             kHighWaterMarkBytes);
+                        finishAsyncPrepare_l();
+                    }
+                }
             }
-
-            notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
-
-            lowWatermark = 2 * bitrate / 8;  // 2 secs
-            highWatermark = 10 * bitrate / 8;  // 10 secs
         }
     }
 
-    if ((mFlags & PLAYING) && !eos && (cachedDataRemaining < lowWatermark)) {
-        LOGI("cache is running low (< %d) , pausing.", lowWatermark);
-        mFlags |= CACHE_UNDERRUN;
-        pause_l();
-        notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
-    } else if ((mFlags & CACHE_UNDERRUN)
-            && (eos || cachedDataRemaining > highWatermark)) {
-        LOGI("cache has filled up (> %d), resuming.", highWatermark);
-        mFlags &= ~CACHE_UNDERRUN;
-        play_l();
-        notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+    int64_t cachedDurationUs;
+    bool eos;
+    if (getCachedDuration_l(&cachedDurationUs, &eos)) {
+        if ((mFlags & PLAYING) && !eos
+                && (cachedDurationUs < kLowWaterMarkUs)) {
+            LOGI("cache is running low (%.2f secs) , pausing.",
+                 cachedDurationUs / 1E6);
+            mFlags |= CACHE_UNDERRUN;
+            pause_l();
+            notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
+        } else if (eos || cachedDurationUs > kHighWaterMarkUs) {
+            if (mFlags & CACHE_UNDERRUN) {
+                LOGI("cache has filled up (%.2f secs), resuming.",
+                     cachedDurationUs / 1E6);
+                mFlags &= ~CACHE_UNDERRUN;
+                play_l();
+                notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+            } else if (mFlags & PREPARING) {
+                LOGV("cache has filled up (%.2f secs), prepare is done",
+                     cachedDurationUs / 1E6);
+                finishAsyncPrepare_l();
+            }
+        }
     }
 
     postBufferingEvent_l();
@@ -537,7 +597,7 @@
         return;
     }
 
-    if (mFlags & LOOPING) {
+    if (mFlags & (LOOPING | AUTO_LOOPING)) {
         seekTo_l(0);
 
         if (mVideoSource != NULL) {
@@ -1432,45 +1492,49 @@
 }
 
 void AwesomePlayer::onPrepareAsyncEvent() {
-    {
-        Mutex::Autolock autoLock(mLock);
+    Mutex::Autolock autoLock(mLock);
 
-        if (mFlags & PREPARE_CANCELLED) {
-            LOGI("prepare was cancelled before doing anything");
-            abortPrepare(UNKNOWN_ERROR);
+    if (mFlags & PREPARE_CANCELLED) {
+        LOGI("prepare was cancelled before doing anything");
+        abortPrepare(UNKNOWN_ERROR);
+        return;
+    }
+
+    if (mUri.size() > 0) {
+        status_t err = finishSetDataSource_l();
+
+        if (err != OK) {
+            abortPrepare(err);
             return;
         }
-
-        if (mUri.size() > 0) {
-            status_t err = finishSetDataSource_l();
-
-            if (err != OK) {
-                abortPrepare(err);
-                return;
-            }
-        }
-
-        if (mVideoTrack != NULL && mVideoSource == NULL) {
-            status_t err = initVideoDecoder();
-
-            if (err != OK) {
-                abortPrepare(err);
-                return;
-            }
-        }
-
-        if (mAudioTrack != NULL && mAudioSource == NULL) {
-            status_t err = initAudioDecoder();
-
-            if (err != OK) {
-                abortPrepare(err);
-                return;
-            }
-        }
     }
 
-    Mutex::Autolock autoLock(mLock);
+    if (mVideoTrack != NULL && mVideoSource == NULL) {
+        status_t err = initVideoDecoder();
 
+        if (err != OK) {
+            abortPrepare(err);
+            return;
+        }
+    }
+
+    if (mAudioTrack != NULL && mAudioSource == NULL) {
+        status_t err = initAudioDecoder();
+
+        if (err != OK) {
+            abortPrepare(err);
+            return;
+        }
+    }
+
+    if (mCachedSource != NULL || mRTSPController != NULL) {
+        postBufferingEvent_l();
+    } else {
+        finishAsyncPrepare_l();
+    }
+}
+
+void AwesomePlayer::finishAsyncPrepare_l() {
     if (mIsAsyncPrepare) {
         if (mVideoSource == NULL) {
             notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
@@ -1486,8 +1550,6 @@
     mFlags |= PREPARED;
     mAsyncPrepareEvent = NULL;
     mPreparedCondition.broadcast();
-
-    postBufferingEvent_l();
 }
 
 status_t AwesomePlayer::suspend() {
@@ -1527,7 +1589,7 @@
     state->mUriHeaders = mUriHeaders;
     state->mFileSource = mFileSource;
 
-    state->mFlags = mFlags & (PLAYING | LOOPING | AT_EOS);
+    state->mFlags = mFlags & (PLAYING | AUTO_LOOPING | LOOPING | AT_EOS);
     getPosition(&state->mPositionUs);
 
     if (mLastVideoBuffer) {
@@ -1588,7 +1650,7 @@
 
     seekTo_l(state->mPositionUs);
 
-    mFlags = state->mFlags & (LOOPING | AT_EOS);
+    mFlags = state->mFlags & (AUTO_LOOPING | LOOPING | AT_EOS);
 
     if (state->mLastVideoFrame && (mSurface != NULL || mISurface != NULL)) {
         mVideoRenderer =
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index ba99501..7595a28 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -37,7 +37,7 @@
 namespace android {
 
 // static
-CameraSourceTimeLapse *CameraSourceTimeLapse::Create(bool useStillCameraForTimeLapse,
+CameraSourceTimeLapse *CameraSourceTimeLapse::Create(
         int64_t timeBetweenTimeLapseFrameCaptureUs,
         int32_t width, int32_t height,
         int32_t videoFrameRate) {
@@ -47,13 +47,12 @@
         return NULL;
     }
 
-    return new CameraSourceTimeLapse(camera, useStillCameraForTimeLapse,
-            timeBetweenTimeLapseFrameCaptureUs, width, height, videoFrameRate);
+    return new CameraSourceTimeLapse(camera, timeBetweenTimeLapseFrameCaptureUs,
+            width, height, videoFrameRate);
 }
 
 // static
 CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera(const sp<Camera> &camera,
-        bool useStillCameraForTimeLapse,
         int64_t timeBetweenTimeLapseFrameCaptureUs,
         int32_t width, int32_t height,
         int32_t videoFrameRate) {
@@ -61,17 +60,15 @@
         return NULL;
     }
 
-    return new CameraSourceTimeLapse(camera, useStillCameraForTimeLapse,
-            timeBetweenTimeLapseFrameCaptureUs, width, height, videoFrameRate);
+    return new CameraSourceTimeLapse(camera, timeBetweenTimeLapseFrameCaptureUs,
+            width, height, videoFrameRate);
 }
 
 CameraSourceTimeLapse::CameraSourceTimeLapse(const sp<Camera> &camera,
-        bool useStillCameraForTimeLapse,
         int64_t timeBetweenTimeLapseFrameCaptureUs,
         int32_t width, int32_t height,
         int32_t videoFrameRate)
     : CameraSource(camera),
-      mUseStillCameraForTimeLapse(useStillCameraForTimeLapse),
       mTimeBetweenTimeLapseFrameCaptureUs(timeBetweenTimeLapseFrameCaptureUs),
       mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate),
       mLastTimeLapseFrameRealTimestampUs(0),
@@ -80,7 +77,13 @@
     LOGV("starting time lapse mode");
     mVideoWidth = width;
     mVideoHeight = height;
-    if (mUseStillCameraForTimeLapse) {
+
+    if (trySettingPreviewSize(width, height)) {
+        mUseStillCameraForTimeLapse = false;
+    } else {
+        // TODO: Add a check to see that mTimeBetweenTimeLapseFrameCaptureUs is greater
+        // than the fastest rate at which the still camera can take pictures.
+        mUseStillCameraForTimeLapse = true;
         CHECK(setPictureSizeToClosestSupported(width, height));
         mNeedCropping = computeCropRectangleOffset();
         mMeta->setInt32(kKeyWidth, width);
@@ -91,6 +94,35 @@
 CameraSourceTimeLapse::~CameraSourceTimeLapse() {
 }
 
+bool CameraSourceTimeLapse::trySettingPreviewSize(int32_t width, int32_t height) {
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    String8 s = mCamera->getParameters();
+    IPCThreadState::self()->restoreCallingIdentity(token);
+
+    CameraParameters params(s);
+    Vector<Size> supportedSizes;
+    params.getSupportedPreviewSizes(supportedSizes);
+
+    bool previewSizeSupported = false;
+    for (uint32_t i = 0; i < supportedSizes.size(); ++i) {
+        int32_t pictureWidth = supportedSizes[i].width;
+        int32_t pictureHeight = supportedSizes[i].height;
+
+        if ((pictureWidth == width) && (pictureHeight == height)) {
+            previewSizeSupported = true;
+        }
+    }
+
+    if (previewSizeSupported) {
+        LOGV("Video size (%d, %d) is a supported preview size", width, height);
+        params.setPreviewSize(width, height);
+        CHECK(mCamera->setParameters(params.flatten()));
+        return true;
+    }
+
+    return false;
+}
+
 bool CameraSourceTimeLapse::setPictureSizeToClosestSupported(int32_t width, int32_t height) {
     int64_t token = IPCThreadState::self()->clearCallingIdentity();
     String8 s = mCamera->getParameters();
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index a15b84e..806836d 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -32,12 +32,12 @@
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/Utils.h>
 #include <media/mediarecorder.h>
-#include <cutils/properties.h>
 
 #include "include/ESDS.h"
 
 namespace android {
 
+static const int64_t kMax32BitFileSize = 0x007fffffffLL;
 static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
 static const uint8_t kNalUnitTypePicParamSet = 0x08;
 
@@ -59,7 +59,7 @@
     bool isAvc() const { return mIsAvc; }
     bool isAudio() const { return mIsAudio; }
     bool isMPEG4() const { return mIsMPEG4; }
-    void addChunkOffset(off_t offset) { mChunkOffsets.push_back(offset); }
+    void addChunkOffset(off_t offset);
     status_t dump(int fd, const Vector<String16>& args) const;
 
 private:
@@ -79,7 +79,7 @@
     bool mIsRealTimeRecording;
     int64_t mMaxTimeStampUs;
     int64_t mEstimatedTrackSizeBytes;
-    int64_t mMaxWriteTimeUs;
+    int64_t mMdatSizeBytes;
     int32_t mTimeScale;
 
     pthread_t mThread;
@@ -92,8 +92,11 @@
     bool                mSamplesHaveSameSize;
 
     List<MediaBuffer *> mChunkSamples;
+
+    size_t              mNumStcoTableEntries;
     List<off_t>         mChunkOffsets;
 
+    size_t              mNumStscTableEntries;
     struct StscTableEntry {
 
         StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id)
@@ -107,9 +110,10 @@
     };
     List<StscTableEntry> mStscTableEntries;
 
+    size_t        mNumStssTableEntries;
     List<int32_t> mStssTableEntries;
-    List<int64_t> mChunkDurations;
 
+    size_t        mNumSttsTableEntries;
     struct SttsTableEntry {
 
         SttsTableEntry(uint32_t count, uint32_t durationUs)
@@ -161,12 +165,6 @@
     void trackProgressStatus(int64_t timeUs, status_t err = OK);
     void initTrackingProgressStatus(MetaData *params);
 
-    // Utilities for collecting statistical data
-    void logStatisticalData(bool isAudio);
-    void findMinAvgMaxSampleDurationMs(
-            int32_t *min, int32_t *avg, int32_t *max);
-    void findMinMaxChunkDurations(int64_t *min, int64_t *max);
-
     void getCodecSpecificDataFromInputFormatIfPossible();
 
     // Determine the track time scale
@@ -178,14 +176,18 @@
     // Simple validation on the codec specific data
     status_t checkCodecSpecificData() const;
 
+    void updateTrackSizeEstimate();
+    void addOneStscTableEntry(size_t chunkId, size_t sampleId);
+    void addOneStssTableEntry(size_t sampleId);
+    void addOneSttsTableEntry(size_t sampleCount, int64_t durationUs);
+
     Track(const Track &);
     Track &operator=(const Track &);
 };
 
-#define USE_NALLEN_FOUR         1
-
 MPEG4Writer::MPEG4Writer(const char *filename)
     : mFile(fopen(filename, "wb")),
+      mUse4ByteNalLength(true),
       mUse32BitOffset(true),
       mPaused(false),
       mStarted(false),
@@ -198,6 +200,7 @@
 
 MPEG4Writer::MPEG4Writer(int fd)
     : mFile(fdopen(fd, "wb")),
+      mUse4ByteNalLength(true),
       mUse32BitOffset(true),
       mPaused(false),
       mStarted(false),
@@ -211,9 +214,11 @@
 MPEG4Writer::~MPEG4Writer() {
     stop();
 
-    for (List<Track *>::iterator it = mTracks.begin();
-         it != mTracks.end(); ++it) {
+    while (!mTracks.empty()) {
+        List<Track *>::iterator it = mTracks.begin();
         delete *it;
+        (*it) = NULL;
+        mTracks.erase(it);
     }
     mTracks.clear();
 }
@@ -332,11 +337,26 @@
         mUse32BitOffset = false;
     }
 
-    // System property can overwrite the file offset bits parameter
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("media.stagefright.record-64bits", value, NULL)
-        && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
-        mUse32BitOffset = false;
+    if (mUse32BitOffset) {
+        // Implicit 32 bit file size limit
+        if (mMaxFileSizeLimitBytes == 0) {
+            mMaxFileSizeLimitBytes = kMax32BitFileSize;
+        }
+
+        // If file size is set to be larger than the 32 bit file
+        // size limit, treat it as an error.
+        if (mMaxFileSizeLimitBytes > kMax32BitFileSize) {
+            LOGE("32-bit file size limit too big: %lld bytes",
+                mMaxFileSizeLimitBytes);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    int32_t use2ByteNalLength;
+    if (param &&
+        param->findInt32(kKey2ByteNalLength, &use2ByteNalLength) &&
+        use2ByteNalLength) {
+        mUse4ByteNalLength = false;
     }
 
     mStartTimestampUs = -1;
@@ -413,6 +433,10 @@
     return OK;
 }
 
+bool MPEG4Writer::use32BitFileOffset() const {
+    return mUse32BitOffset;
+}
+
 status_t MPEG4Writer::pause() {
     if (mFile == NULL) {
         return OK;
@@ -607,32 +631,30 @@
 
     size_t length = buffer->range_length();
 
-#if USE_NALLEN_FOUR
-    uint8_t x = length >> 24;
-    fwrite(&x, 1, 1, mFile);
-    x = (length >> 16) & 0xff;
-    fwrite(&x, 1, 1, mFile);
-    x = (length >> 8) & 0xff;
-    fwrite(&x, 1, 1, mFile);
-    x = length & 0xff;
-    fwrite(&x, 1, 1, mFile);
-#else
-    CHECK(length < 65536);
+    if (mUse4ByteNalLength) {
+        uint8_t x = length >> 24;
+        fwrite(&x, 1, 1, mFile);
+        x = (length >> 16) & 0xff;
+        fwrite(&x, 1, 1, mFile);
+        x = (length >> 8) & 0xff;
+        fwrite(&x, 1, 1, mFile);
+        x = length & 0xff;
+        fwrite(&x, 1, 1, mFile);
 
-    uint8_t x = length >> 8;
-    fwrite(&x, 1, 1, mFile);
-    x = length & 0xff;
-    fwrite(&x, 1, 1, mFile);
-#endif
+        fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
+                1, length, mFile);
+        mOffset += length + 4;
+    } else {
+        CHECK(length < 65536);
 
-    fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
-           1, length, mFile);
-
-#if USE_NALLEN_FOUR
-    mOffset += length + 4;
-#else
-    mOffset += length + 2;
-#endif
+        uint8_t x = length >> 8;
+        fwrite(&x, 1, 1, mFile);
+        x = length & 0xff;
+        fwrite(&x, 1, 1, mFile);
+        fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
+                1, length, mFile);
+        mOffset += length + 2;
+    }
 
     return old_offset;
 }
@@ -739,7 +761,8 @@
          it != mTracks.end(); ++it) {
         nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
     }
-    return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes);
+
+    return (nTotalBytesEstimate  + 1024 >= mMaxFileSizeLimitBytes);
 }
 
 bool MPEG4Writer::exceedsFileDurationLimit() {
@@ -819,6 +842,48 @@
     setTimeScale();
 }
 
+void MPEG4Writer::Track::updateTrackSizeEstimate() {
+
+    int64_t stcoBoxSizeBytes = mOwner->use32BitFileOffset()
+                                ? mNumStcoTableEntries * 4
+                                : mNumStcoTableEntries * 8;
+
+    int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mNumSamples * 4);
+
+    mEstimatedTrackSizeBytes = mMdatSizeBytes +             // media data size
+                               mNumStscTableEntries * 12 +  // stsc box size
+                               mNumStssTableEntries * 4 +   // stss box size
+                               mNumSttsTableEntries * 8 +   // stts box size
+                               stcoBoxSizeBytes +           // stco box size
+                               stszBoxSizeBytes;            // stsz box size
+}
+
+void MPEG4Writer::Track::addOneStscTableEntry(
+        size_t chunkId, size_t sampleId) {
+
+        StscTableEntry stscEntry(chunkId, sampleId, 1);
+        mStscTableEntries.push_back(stscEntry);
+        ++mNumStscTableEntries;
+}
+
+void MPEG4Writer::Track::addOneStssTableEntry(size_t sampleId) {
+    mStssTableEntries.push_back(sampleId);
+    ++mNumStssTableEntries;
+}
+
+void MPEG4Writer::Track::addOneSttsTableEntry(
+        size_t sampleCount, int64_t durationUs) {
+
+    SttsTableEntry sttsEntry(sampleCount, durationUs);
+    mSttsTableEntries.push_back(sttsEntry);
+    ++mNumSttsTableEntries;
+}
+
+void MPEG4Writer::Track::addChunkOffset(off_t offset) {
+    ++mNumStcoTableEntries;
+    mChunkOffsets.push_back(offset);
+}
+
 void MPEG4Writer::Track::setTimeScale() {
     LOGV("setTimeScale");
     // Default time scale
@@ -1039,6 +1104,7 @@
     return OK;
 }
 
+
 status_t MPEG4Writer::Track::start(MetaData *params) {
     if (!mDone && mPaused) {
         mPaused = false;
@@ -1077,6 +1143,11 @@
     mTrackDurationUs = 0;
     mReachedEOS = false;
     mEstimatedTrackSizeBytes = 0;
+    mNumStcoTableEntries = 0;
+    mNumStssTableEntries = 0;
+    mNumStscTableEntries = 0;
+    mNumSttsTableEntries = 0;
+    mMdatSizeBytes = 0;
 
     pthread_create(&mThread, &attr, ThreadWrapper, this);
     pthread_attr_destroy(&attr);
@@ -1123,46 +1194,6 @@
     return (void *) err;
 }
 
-#include <ctype.h>
-static void hexdump(const void *_data, size_t size) {
-    const uint8_t *data = (const uint8_t *)_data;
-    size_t offset = 0;
-    while (offset < size) {
-        printf("0x%04x  ", offset);
-
-        size_t n = size - offset;
-        if (n > 16) {
-            n = 16;
-        }
-
-        for (size_t i = 0; i < 16; ++i) {
-            if (i == 8) {
-                printf(" ");
-            }
-
-            if (offset + i < size) {
-                printf("%02x ", data[offset + i]);
-            } else {
-                printf("   ");
-            }
-        }
-
-        printf(" ");
-
-        for (size_t i = 0; i < n; ++i) {
-            if (isprint(data[offset + i])) {
-                printf("%c", data[offset + i]);
-            } else {
-                printf(".");
-            }
-        }
-
-        printf("\n");
-
-        offset += 16;
-    }
-}
-
 static void getNalUnitType(uint8_t byte, uint8_t* type) {
     LOGV("getNalUnitType: %d", byte);
 
@@ -1334,7 +1365,6 @@
 
 status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
         const uint8_t *data, size_t size) {
-    // hexdump(data, size);
 
     if (mCodecSpecificData != NULL) {
         LOGE("Already have codec specific data");
@@ -1365,11 +1395,11 @@
     header[3] = mLevelIdc;
 
     // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne
-#if USE_NALLEN_FOUR
-    header[4] = 0xfc | 3;  // length size == 4 bytes
-#else
-    header[4] = 0xfc | 1;  // length size == 2 bytes
-#endif
+    if (mOwner->useNalLengthFour()) {
+        header[4] = 0xfc | 3;  // length size == 4 bytes
+    } else {
+        header[4] = 0xfc | 1;  // length size == 2 bytes
+    }
 
     // 3-bit '111' followed by 5-bit numSequenceParameterSets
     int nSequenceParamSets = mSeqParamSets.size();
@@ -1406,15 +1436,6 @@
     return OK;
 }
 
-static bool collectStatisticalData() {
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("media.stagefright.record-stats", value, NULL)
-        && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
-        return true;
-    }
-    return false;
-}
-
 status_t MPEG4Writer::Track::threadEntry() {
     int32_t count = 0;
     const int64_t interleaveDurationUs = mOwner->interleaveDuration();
@@ -1430,14 +1451,9 @@
     int64_t previousPausedDurationUs = 0;
     int64_t timestampUs;
 
-    int64_t wallClockTimeUs = 0;
-    int64_t lastWallClockTimeUs = 0;
-
     sp<MetaData> meta_data;
-    bool collectStats = collectStatisticalData();
 
     mNumSamples = 0;
-    mMaxWriteTimeUs = 0;
     status_t err = OK;
     MediaBuffer *buffer;
     while (!mDone && (err = mSource->read(&buffer)) == OK) {
@@ -1498,17 +1514,19 @@
 
         if (mIsAvc) StripStartcode(copy);
 
-        size_t sampleSize;
-        sampleSize = mIsAvc
-#if USE_NALLEN_FOUR
-                ? copy->range_length() + 4
-#else
-                ? copy->range_length() + 2
-#endif
-                : copy->range_length();
+        size_t sampleSize = copy->range_length();
+        if (mIsAvc) {
+            if (mOwner->useNalLengthFour()) {
+                sampleSize += 4;
+            } else {
+                sampleSize += 2;
+            }
+        }
 
         // Max file size or duration handling
-        mEstimatedTrackSizeBytes += sampleSize;
+        mMdatSizeBytes += sampleSize;
+        updateTrackSizeEstimate();
+
         if (mOwner->exceedsFileSizeLimit()) {
             mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
             break;
@@ -1542,14 +1560,15 @@
             // of neighboring samples. This in turn helps reduce the track header size,
             // especially, the number of entries in the "stts" box.
             if (mNumSamples > 1) {
-                int64_t durationUs = timestampUs + mOwner->getDriftTimeUs() - lastTimestampUs;
+                int64_t currDriftTimeUs = mOwner->getDriftTimeUs();
+                int64_t durationUs = timestampUs + currDriftTimeUs - lastTimestampUs;
                 int64_t diffUs = (durationUs > lastDurationUs)
                             ? durationUs - lastDurationUs
                             : lastDurationUs - durationUs;
                 if (diffUs <= 5000) {  // XXX: Magic number 5ms
                     timestampUs = lastTimestampUs + lastDurationUs;
                 } else {
-                    timestampUs += mOwner->getDriftTimeUs();
+                    timestampUs += currDriftTimeUs;
                 }
             }
         }
@@ -1557,12 +1576,6 @@
         if (mNumSamples > 1) {
             if (timestampUs <= lastTimestampUs) {
                 LOGW("Frame arrives too late!");
-#if 0
-                // Drop the late frame.
-                copy->release();
-                copy = NULL;
-                continue;
-#else
                 // Don't drop the late frame, since dropping a frame may cause
                 // problems later during playback
 
@@ -1573,7 +1586,6 @@
                 } else {
                     timestampUs = lastTimestampUs + (1000000LL + (mTimeScale >> 1)) / mTimeScale;
                 }
-#endif
             }
         }
 
@@ -1596,8 +1608,7 @@
                      (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
 
             if (currDurationTicks != lastDurationTicks) {
-                SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
-                mSttsTableEntries.push_back(sttsEntry);
+                addOneSttsTableEntry(sampleCount, lastDurationUs);
                 sampleCount = 1;
             } else {
                 ++sampleCount;
@@ -1613,16 +1624,14 @@
         lastDurationTicks = currDurationTicks;
         lastTimestampUs = timestampUs;
         if (mIsRealTimeRecording && mIsAudio) {
-            wallClockTimeUs = systemTime() / 1000;
-            int64_t wallClockDurationUs = wallClockTimeUs - lastWallClockTimeUs;
-            if (mNumSamples > 2) {
-                mOwner->addDriftTimeUs(lastDurationUs - wallClockDurationUs);
+            int64_t driftTimeUs = 0;
+            if (meta_data->findInt64(kKeyDriftTime, &driftTimeUs)) {
+                mOwner->setDriftTimeUs(driftTimeUs);
             }
-            lastWallClockTimeUs = wallClockTimeUs;
         }
 
         if (isSync != 0) {
-            mStssTableEntries.push_back(mNumSamples);
+            addOneStssTableEntry(mNumSamples);
         }
 
         if (mTrackingProgressStatus) {
@@ -1635,7 +1644,7 @@
             off_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
                                  : mOwner->addSample_l(copy);
             if (mChunkOffsets.empty()) {
-                mChunkOffsets.push_back(offset);
+                addChunkOffset(offset);
             }
             copy->release();
             copy = NULL;
@@ -1644,8 +1653,7 @@
 
         mChunkSamples.push_back(copy);
         if (interleaveDurationUs == 0) {
-            StscTableEntry stscEntry(++nChunks, 1, 1);
-            mStscTableEntries.push_back(stscEntry);
+            addOneStscTableEntry(++nChunks, 1);
             bufferChunk(timestampUs);
         } else {
             if (chunkTimestampUs == 0) {
@@ -1653,15 +1661,10 @@
             } else {
                 if (timestampUs - chunkTimestampUs > interleaveDurationUs) {
                     ++nChunks;
-                    if (collectStats) {
-                        mChunkDurations.push_back(timestampUs - chunkTimestampUs);
-                    }
                     if (nChunks == 1 ||  // First chunk
                         (--(mStscTableEntries.end()))->samplesPerChunk !=
                          mChunkSamples.size()) {
-                        StscTableEntry stscEntry(nChunks,
-                                mChunkSamples.size(), 1);
-                        mStscTableEntries.push_back(stscEntry);
+                        addOneStscTableEntry(nChunks, mChunkSamples.size());
                     }
                     bufferChunk(timestampUs);
                     chunkTimestampUs = timestampUs;
@@ -1680,12 +1683,9 @@
 
     // Last chunk
     if (mOwner->numTracks() == 1) {
-        StscTableEntry stscEntry(1, mNumSamples, 1);
-        mStscTableEntries.push_back(stscEntry);
+        addOneStscTableEntry(1, mNumSamples);
     } else if (!mChunkSamples.empty()) {
-        ++nChunks;
-        StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1);
-        mStscTableEntries.push_back(stscEntry);
+        addOneStscTableEntry(++nChunks, mChunkSamples.size());
         bufferChunk(timestampUs);
     }
 
@@ -1697,14 +1697,12 @@
     } else {
         ++sampleCount;  // Count for the last sample
     }
-    SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
-    mSttsTableEntries.push_back(sttsEntry);
+    addOneSttsTableEntry(sampleCount, lastDurationUs);
     mTrackDurationUs += lastDurationUs;
     mReachedEOS = true;
-    LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. Max write time: %lld us - %s",
-            count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
+    LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
+            count, nZeroLengthFrames, mNumSamples, mIsAudio? "audio": "video");
 
-    logStatisticalData(mIsAudio);
     if (err == ERROR_END_OF_STREAM) {
         return OK;
     }
@@ -1729,17 +1727,6 @@
     CHECK(nTracks < 64);  // Arbitrary number
 
     int32_t trackNum = 0;
-#if 0
-    // In the worst case, we can put the trackNum
-    // along with MEDIA_RECORDER_INFO_COMPLETION_STATUS
-    // to report the progress.
-    for (List<Track *>::iterator it = mTracks.begin();
-         it != mTracks.end(); ++it, ++trackNum) {
-        if (track == (*it)) {
-            break;
-        }
-    }
-#endif
     CHECK(trackNum < nTracks);
     trackNum <<= 16;
 
@@ -1766,95 +1753,10 @@
     }
 }
 
-void MPEG4Writer::Track::findMinAvgMaxSampleDurationMs(
-        int32_t *min, int32_t *avg, int32_t *max) {
-    CHECK(!mSampleSizes.empty());
-    int32_t avgSampleDurationMs = mTrackDurationUs / 1000 / mNumSamples;
-    int32_t minSampleDurationMs = 0x7FFFFFFF;
-    int32_t maxSampleDurationMs = 0;
-    for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
-        it != mSttsTableEntries.end(); ++it) {
-        int32_t sampleDurationMs =
-            (static_cast<int32_t>(it->sampleDurationUs) + 500) / 1000;
-        if (sampleDurationMs > maxSampleDurationMs) {
-            maxSampleDurationMs = sampleDurationMs;
-        } else if (sampleDurationMs < minSampleDurationMs) {
-            minSampleDurationMs = sampleDurationMs;
-        }
-        LOGI("sample duration: %d ms", sampleDurationMs);
-    }
-    CHECK(minSampleDurationMs != 0);
-    CHECK(avgSampleDurationMs != 0);
-    CHECK(maxSampleDurationMs != 0);
-    *min = minSampleDurationMs;
-    *avg = avgSampleDurationMs;
-    *max = maxSampleDurationMs;
-}
-
-// Don't count the last duration
-void MPEG4Writer::Track::findMinMaxChunkDurations(int64_t *min, int64_t *max) {
-    int64_t duration = mOwner->interleaveDuration();
-    int64_t minChunkDuration = duration;
-    int64_t maxChunkDuration = duration;
-    if (mChunkDurations.size() > 1) {
-        for (List<int64_t>::iterator it = mChunkDurations.begin();
-            it != --mChunkDurations.end(); ++it) {
-            if (minChunkDuration > (*it)) {
-                minChunkDuration = (*it);
-            } else if (maxChunkDuration < (*it)) {
-                maxChunkDuration = (*it);
-            }
-        }
-    }
-    *min = minChunkDuration;
-    *max = maxChunkDuration;
-}
-
-void MPEG4Writer::Track::logStatisticalData(bool isAudio) {
-    if (mTrackDurationUs <= 0 || mSampleSizes.empty()) {
-        LOGI("nothing is recorded");
-        return;
-    }
-
-    bool collectStats = collectStatisticalData();
-
-    if (collectStats) {
-        LOGI("%s track - duration %lld us, total %d frames",
-                isAudio? "audio": "video", mTrackDurationUs,
-                mNumSamples);
-        int32_t min, avg, max;
-        findMinAvgMaxSampleDurationMs(&min, &avg, &max);
-        LOGI("min/avg/max sample duration (ms): %d/%d/%d", min, avg, max);
-        if (!isAudio) {
-            float avgFps = 1000.0 / avg;
-            float minFps = 1000.0 / max;
-            float maxFps = 1000.0 / min;
-            LOGI("min/avg/max frame rate (fps): %.2f/%.2f/%.2f",
-                minFps, avgFps, maxFps);
-        }
-
-        int64_t totalBytes = 0;
-        for (List<size_t>::iterator it = mSampleSizes.begin();
-            it != mSampleSizes.end(); ++it) {
-            totalBytes += (*it);
-        }
-        float bitRate = (totalBytes * 8000000.0) / mTrackDurationUs;
-        LOGI("avg bit rate (bps): %.2f", bitRate);
-
-        int64_t duration = mOwner->interleaveDuration();
-        if (duration != 0) {  // If interleaving is enabled
-            int64_t minChunk, maxChunk;
-            findMinMaxChunkDurations(&minChunk, &maxChunk);
-            LOGI("min/avg/max chunk duration (ms): %lld/%lld/%lld",
-                minChunk, duration, maxChunk);
-        }
-    }
-}
-
-void MPEG4Writer::addDriftTimeUs(int64_t driftTimeUs) {
-    LOGV("addDriftTimeUs: %lld us", driftTimeUs);
+void MPEG4Writer::setDriftTimeUs(int64_t driftTimeUs) {
+    LOGV("setDriftTimeUs: %lld us", driftTimeUs);
     Mutex::Autolock autolock(mLock);
-    mDriftTimeUs += driftTimeUs;
+    mDriftTimeUs = driftTimeUs;
 }
 
 int64_t MPEG4Writer::getDriftTimeUs() {
@@ -1863,17 +1765,16 @@
     return mDriftTimeUs;
 }
 
+bool MPEG4Writer::useNalLengthFour() {
+    return mUse4ByteNalLength;
+}
+
 void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
     LOGV("bufferChunk");
 
-    int64_t startTimeUs = systemTime() / 1000;
     Chunk chunk(this, timestampUs, mChunkSamples);
     mOwner->bufferChunk(chunk);
     mChunkSamples.clear();
-    int64_t endTimeUs = systemTime() / 1000;
-    if (mMaxWriteTimeUs < endTimeUs - startTimeUs) {
-        mMaxWriteTimeUs = endTimeUs - startTimeUs;
-    }
 }
 
 int64_t MPEG4Writer::Track::getDurationUs() const {
@@ -2226,7 +2127,7 @@
 
           mOwner->beginBox("stts");
             mOwner->writeInt32(0);  // version=0, flags=0
-            mOwner->writeInt32(mSttsTableEntries.size());
+            mOwner->writeInt32(mNumSttsTableEntries);
             int64_t prevTimestampUs = 0;
             for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
                  it != mSttsTableEntries.end(); ++it) {
@@ -2246,7 +2147,7 @@
           if (!mIsAudio) {
             mOwner->beginBox("stss");
               mOwner->writeInt32(0);  // version=0, flags=0
-              mOwner->writeInt32(mStssTableEntries.size());  // number of sync frames
+              mOwner->writeInt32(mNumStssTableEntries);  // number of sync frames
               for (List<int32_t>::iterator it = mStssTableEntries.begin();
                    it != mStssTableEntries.end(); ++it) {
                   mOwner->writeInt32(*it);
@@ -2273,7 +2174,7 @@
 
           mOwner->beginBox("stsc");
             mOwner->writeInt32(0);  // version=0, flags=0
-            mOwner->writeInt32(mStscTableEntries.size());
+            mOwner->writeInt32(mNumStscTableEntries);
             for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
                  it != mStscTableEntries.end(); ++it) {
                 mOwner->writeInt32(it->firstChunk);
@@ -2283,7 +2184,7 @@
           mOwner->endBox();  // stsc
           mOwner->beginBox(use32BitOffset? "stco": "co64");
             mOwner->writeInt32(0);  // version=0, flags=0
-            mOwner->writeInt32(mChunkOffsets.size());
+            mOwner->writeInt32(mNumStcoTableEntries);
             for (List<off_t>::iterator it = mChunkOffsets.begin();
                  it != mChunkOffsets.end(); ++it) {
                 if (use32BitOffset) {
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 2c1311a..7a8cf32 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -592,6 +592,7 @@
         { "DATE", kKeyDate },
         { "LYRICIST", kKeyWriter },
         { "METADATA_BLOCK_PICTURE", kKeyAlbumArt },
+        { "ANDROID_LOOP", kKeyAutoLoop },
     };
 
     for (int i = 0; i < mVc.comments; ++i) {
@@ -605,12 +606,15 @@
                     extractAlbumArt(
                             &comment[tagLen + 1],
                             mVc.comment_lengths[i] - tagLen - 1);
+                } else if (kMap[j].mKey == kKeyAutoLoop) {
+                    if (!strcasecmp(&comment[tagLen + 1], "true")) {
+                        mFileMeta->setInt32(kKeyAutoLoop, true);
+                    }
                 } else {
                     mFileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
                 }
             }
         }
-
     }
 
 #if 0
diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
index 052c354..e391c72 100644
--- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp
+++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
@@ -208,6 +208,8 @@
     MediaBuffer *buffer;
     CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);
     uint8_t *outPtr = (uint8_t *)buffer->data();
+    bool readFromSource = false;
+    int64_t wallClockTimeUs = -1;
 
     if (mFrameCount == 0) {
         memcpy(outPtr, mAudioSpecificConfigData, 2);
@@ -238,9 +240,15 @@
             CHECK_EQ(align, 0);
 
             int64_t timeUs;
+            if (mInputBuffer->meta_data()->findInt64(kKeyDriftTime, &timeUs)) {
+                wallClockTimeUs = timeUs;
+            }
             if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
                 mAnchorTimeUs = timeUs;
             }
+            readFromSource = true;
+        } else {
+            readFromSource = false;
         }
         size_t copy =
             (kNumSamplesPerFrame - mNumInputSamples) * sizeof(int16_t);
@@ -288,9 +296,13 @@
     CHECK(outputData.Length != 0);
     buffer->set_range(0, outputData.Length);
 
-    int64_t timestampUs = ((mFrameCount - 1) * 1000000LL * kNumSamplesPerFrame) / mSampleRate;
+    int64_t mediaTimeUs =
+        ((mFrameCount - 1) * 1000000LL * kNumSamplesPerFrame) / mSampleRate;
+    buffer->meta_data()->setInt64(kKeyTime, mAnchorTimeUs + mediaTimeUs);
+    if (readFromSource && wallClockTimeUs != -1) {
+        buffer->meta_data()->setInt64(kKeyDriftTime, mediaTimeUs - wallClockTimeUs);
+    }
     ++mFrameCount;
-    buffer->meta_data()->setInt64(kKeyTime, timestampUs);
 
     *out = buffer;
     return OK;
diff --git a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
index c875426..858e6d0 100644
--- a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
@@ -147,6 +147,8 @@
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     CHECK(options == NULL || !options->getSeekTo(&seekTimeUs, &mode));
+    bool readFromSource = false;
+    int64_t wallClockTimeUs = -1;
 
     while (mNumInputSamples < kNumSamplesPerFrame) {
         if (mInputBuffer == NULL) {
@@ -166,12 +168,17 @@
 
             size_t align = mInputBuffer->range_length() % sizeof(int16_t);
             CHECK_EQ(align, 0);
+            readFromSource = true;
 
             int64_t timeUs;
+            if (mInputBuffer->meta_data()->findInt64(kKeyDriftTime, &timeUs)) {
+                wallClockTimeUs = timeUs;
+            }
             if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
                 mAnchorTimeUs = timeUs;
-                mNumFramesOutput = 0;
             }
+        } else {
+            readFromSource = false;
         }
 
         size_t copy =
@@ -217,8 +224,14 @@
     buffer->set_range(0, res);
 
     // Each frame of 160 samples is 20ms long.
+    int64_t mediaTimeUs = mNumFramesOutput * 20000LL;
     buffer->meta_data()->setInt64(
-            kKeyTime, mAnchorTimeUs + mNumFramesOutput * 20000);
+            kKeyTime, mAnchorTimeUs + mediaTimeUs);
+
+    if (readFromSource && wallClockTimeUs != -1) {
+        buffer->meta_data()->setInt64(kKeyDriftTime,
+            mediaTimeUs - wallClockTimeUs);
+    }
 
     ++mNumFramesOutput;
 
diff --git a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
index 93304d0..cd28413 100644
--- a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
+++ b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
@@ -198,6 +198,8 @@
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     CHECK(options == NULL || !options->getSeekTo(&seekTimeUs, &mode));
+    bool readFromSource = false;
+    int64_t wallClockTimeUs = -1;
 
     while (mNumInputSamples < kNumSamplesPerFrame) {
         if (mInputBuffer == NULL) {
@@ -219,9 +221,15 @@
             CHECK_EQ(align, 0);
 
             int64_t timeUs;
+            if (mInputBuffer->meta_data()->findInt64(kKeyDriftTime, &timeUs)) {
+                wallClockTimeUs = timeUs;
+            }
             if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
                 mAnchorTimeUs = timeUs;
             }
+            readFromSource = true;
+        } else {
+            readFromSource = false;
         }
 
         size_t copy =
@@ -276,10 +284,11 @@
     buffer->set_range(0, outputData.Length);
     ++mNumFramesOutput;
 
-    // XXX: fix timestamp calculation
-    int64_t timestampUs = mNumFramesOutput * 20000LL;
-
-    buffer->meta_data()->setInt64(kKeyTime, timestampUs);
+    int64_t mediaTimeUs = mNumFramesOutput * 20000LL;
+    buffer->meta_data()->setInt64(kKeyTime, mAnchorTimeUs + mediaTimeUs);
+    if (readFromSource && wallClockTimeUs != -1) {
+        buffer->meta_data()->setInt64(kKeyDriftTime, mediaTimeUs - wallClockTimeUs);
+    }
 
     *out = buffer;
     return OK;
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 507fa5a..c204a94 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -106,7 +106,12 @@
 void SoftwareRenderer::render(
         const void *data, size_t size, void *platformPrivate) {
     android_native_buffer_t *buf;
-    CHECK_EQ(0, mSurface->dequeueBuffer(mSurface.get(), &buf));
+    int err;
+    if ((err = mSurface->dequeueBuffer(mSurface.get(), &buf)) != 0) {
+        LOGW("Surface::dequeueBuffer returned error %d", err);
+        return;
+    }
+
     CHECK_EQ(0, mSurface->lockBuffer(mSurface.get(), buf));
 
     GraphicBufferMapper &mapper = GraphicBufferMapper::get();
@@ -186,7 +191,9 @@
 
     CHECK_EQ(0, mapper.unlock(buf->handle));
 
-    CHECK_EQ(0, mSurface->queueBuffer(mSurface.get(), buf));
+    if ((err = mSurface->queueBuffer(mSurface.get(), buf)) != 0) {
+        LOGW("Surface::queueBuffer returned error %d", err);
+    }
     buf = NULL;
 }
 
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
index 7016880..300d8f7 100644
--- a/media/libstagefright/include/ARTSPController.h
+++ b/media/libstagefright/include/ARTSPController.h
@@ -42,6 +42,7 @@
             size_t index, uint32_t flags);
 
     int64_t getNormalPlayTimeUs();
+    int64_t getQueueDurationUs(bool *eos);
 
     void onMessageReceived(const sp<AMessage> &msg);
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index f34eb45..ea2f7d5 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -107,6 +107,7 @@
         CACHE_UNDERRUN      = 128,
         AUDIO_AT_EOS        = 256,
         VIDEO_AT_EOS        = 512,
+        AUTO_LOOPING        = 1024,
     };
 
     mutable Mutex mLock;
@@ -241,6 +242,9 @@
     void onCheckAudioStatus();
     void onPrepareAsyncEvent();
     void abortPrepare(status_t err);
+    void finishAsyncPrepare_l();
+
+    bool getCachedDuration_l(int64_t *durationUs, bool *eos);
 
     status_t finishSetDataSource_l();
 
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index c99da59..83b75ad 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -59,10 +59,17 @@
             node_id node, OMX_INDEXTYPE index,
             const void *params, size_t size);
 
+    virtual status_t enableGraphicBuffers(
+            node_id node, OMX_U32 port_index, OMX_BOOL enable);
+
     virtual status_t useBuffer(
             node_id node, OMX_U32 port_index, const sp<IMemory> &params,
             buffer_id *buffer);
 
+    virtual status_t useGraphicBuffer(
+            node_id node, OMX_U32 port_index,
+            const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
+
     virtual status_t allocateBuffer(
             node_id node, OMX_U32 port_index, size_t size,
             buffer_id *buffer, void **buffer_data);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index b5b31ac..8c7c562 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -49,10 +49,16 @@
     status_t getConfig(OMX_INDEXTYPE index, void *params, size_t size);
     status_t setConfig(OMX_INDEXTYPE index, const void *params, size_t size);
 
+    status_t enableGraphicBuffers(OMX_U32 portIndex, OMX_BOOL enable);
+
     status_t useBuffer(
             OMX_U32 portIndex, const sp<IMemory> &params,
             OMX::buffer_id *buffer);
 
+    status_t useGraphicBuffer(
+            OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
+            OMX::buffer_id *buffer);
+
     status_t allocateBuffer(
             OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
             void **buffer_data);
@@ -125,4 +131,3 @@
 }  // namespace android
 
 #endif  // OMX_NODE_INSTANCE_H_
-
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 88b9605..37243ad 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -287,6 +287,11 @@
             index, params, size);
 }
 
+status_t OMX::enableGraphicBuffers(
+        node_id node, OMX_U32 port_index, OMX_BOOL enable) {
+    return findInstance(node)->enableGraphicBuffers(port_index, enable);
+}
+
 status_t OMX::useBuffer(
         node_id node, OMX_U32 port_index, const sp<IMemory> &params,
         buffer_id *buffer) {
@@ -294,6 +299,13 @@
             port_index, params, buffer);
 }
 
+status_t OMX::useGraphicBuffer(
+        node_id node, OMX_U32 port_index,
+        const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
+    return findInstance(node)->useGraphicBuffer(
+            port_index, graphicBuffer, buffer);
+}
+
 status_t OMX::allocateBuffer(
         node_id node, OMX_U32 port_index, size_t size,
         buffer_id *buffer, void **buffer_data) {
@@ -530,4 +542,3 @@
 }
 
 }  // namespace android
-
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 5db516e..ba4d765 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -24,6 +24,7 @@
 #include <OMX_Component.h>
 
 #include <binder/IMemory.h>
+#include <media/stagefright/HardwareAPI.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaErrors.h>
 
@@ -40,6 +41,11 @@
           mIsBackup(false) {
     }
 
+    BufferMeta(const sp<GraphicBuffer> &graphicBuffer)
+        : mGraphicBuffer(graphicBuffer),
+          mIsBackup(false) {
+    }
+
     void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
         if (!mIsBackup) {
             return;
@@ -61,6 +67,7 @@
     }
 
 private:
+    sp<GraphicBuffer> mGraphicBuffer;
     sp<IMemory> mMem;
     size_t mSize;
     bool mIsBackup;
@@ -240,6 +247,43 @@
     return StatusFromOMXError(err);
 }
 
+status_t OMXNodeInstance::enableGraphicBuffers(
+        OMX_U32 portIndex, OMX_BOOL enable) {
+    Mutex::Autolock autoLock(mLock);
+
+    OMX_INDEXTYPE index;
+    OMX_ERRORTYPE err = OMX_GetExtensionIndex(
+            mHandle,
+            const_cast<OMX_STRING>("OMX.google.android.index.enableAndroidNativeBuffers"),
+            &index);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_GetExtensionIndex failed");
+
+        return StatusFromOMXError(err);
+    }
+
+    OMX_VERSIONTYPE ver;
+    ver.s.nVersionMajor = 1;
+    ver.s.nVersionMinor = 0;
+    ver.s.nRevision = 0;
+    ver.s.nStep = 0;
+    EnableAndroidNativeBuffersParams params = {
+        sizeof(EnableAndroidNativeBuffersParams), ver, portIndex, enable,
+    };
+
+    err = OMX_SetParameter(mHandle, index, &params);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_EnableAndroidNativeBuffers failed with error %d (0x%08x)",
+                err, err);
+
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
 status_t OMXNodeInstance::useBuffer(
         OMX_U32 portIndex, const sp<IMemory> &params,
         OMX::buffer_id *buffer) {
@@ -273,6 +317,60 @@
     return OK;
 }
 
+status_t OMXNodeInstance::useGraphicBuffer(
+        OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
+        OMX::buffer_id *buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    OMX_INDEXTYPE index;
+    OMX_ERRORTYPE err = OMX_GetExtensionIndex(
+            mHandle,
+            const_cast<OMX_STRING>("OMX.google.android.index.useAndroidNativeBuffer"),
+            &index);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_GetExtensionIndex failed");
+
+        return StatusFromOMXError(err);
+    }
+
+    BufferMeta *bufferMeta = new BufferMeta(graphicBuffer);
+
+    OMX_BUFFERHEADERTYPE *header;
+
+    OMX_VERSIONTYPE ver;
+    ver.s.nVersionMajor = 1;
+    ver.s.nVersionMinor = 0;
+    ver.s.nRevision = 0;
+    ver.s.nStep = 0;
+    UseAndroidNativeBufferParams params = {
+        sizeof(UseAndroidNativeBufferParams), ver, portIndex, bufferMeta,
+        &header, graphicBuffer,
+    };
+
+    err = OMX_SetParameter(mHandle, index, &params);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_UseAndroidNativeBuffer failed with error %d (0x%08x)", err,
+                err);
+
+        delete bufferMeta;
+        bufferMeta = NULL;
+
+        *buffer = 0;
+
+        return UNKNOWN_ERROR;
+    }
+
+    CHECK_EQ(header->pAppPrivate, bufferMeta);
+
+    *buffer = header;
+
+    addActiveBuffer(portIndex, *buffer);
+
+    return OK;
+}
+
 status_t OMXNodeInstance::allocateBuffer(
         OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
         void **buffer_data) {
@@ -498,4 +596,3 @@
 }
 
 }  // namespace android
-
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 75b4571..b63798f 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -746,4 +746,27 @@
     mNormalPlayTimeBaseUs = normalPlayTimeUs;
 }
 
+int64_t APacketSource::getQueueDurationUs(bool *eos) {
+    Mutex::Autolock autoLock(mLock);
+
+    *eos = (mEOSResult != OK);
+
+    if (mBuffers.size() < 2) {
+        return 0;
+    }
+
+    const sp<ABuffer> first = *mBuffers.begin();
+    const sp<ABuffer> last = *--mBuffers.end();
+
+    int64_t firstTimeUs;
+    CHECK(first->meta()->findInt64("timeUs", &firstTimeUs));
+
+    int64_t lastTimeUs;
+    CHECK(last->meta()->findInt64("timeUs", &lastTimeUs));
+
+    CHECK_GE(lastTimeUs, firstTimeUs);
+
+    return lastTimeUs - firstTimeUs;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/rtsp/APacketSource.h b/media/libstagefright/rtsp/APacketSource.h
index 3833ab1..076ddc47 100644
--- a/media/libstagefright/rtsp/APacketSource.h
+++ b/media/libstagefright/rtsp/APacketSource.h
@@ -50,6 +50,8 @@
     void setNormalPlayTimeMapping(
             uint32_t rtpTime, int64_t normalPlayTimeUs);
 
+    int64_t getQueueDurationUs(bool *eos);
+
 protected:
     virtual ~APacketSource();
 
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index a89946b..4c53639 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -143,4 +143,26 @@
     return mHandler->getNormalPlayTimeUs();
 }
 
+int64_t ARTSPController::getQueueDurationUs(bool *eos) {
+    *eos = true;
+
+    int64_t minQueuedDurationUs = 0;
+    for (size_t i = 0; i < mHandler->countTracks(); ++i) {
+        sp<APacketSource> source = mHandler->getPacketSource(i);
+
+        bool newEOS;
+        int64_t queuedDurationUs = source->getQueueDurationUs(&newEOS);
+
+        if (!newEOS) {
+            *eos = false;
+        }
+
+        if (i == 0 || queuedDurationUs < minQueuedDurationUs) {
+            minQueuedDurationUs = queuedDurationUs;
+        }
+    }
+
+    return minQueuedDurationUs;
+}
+
 }  // namespace android
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index bd68f75..fca0142 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -342,7 +342,7 @@
     MtpResponseCode ret = readResponse();
     if (ret == MTP_RESPONSE_OK) {
         MtpProperty* property = new MtpProperty;
-        property->read(mData, true);
+        property->read(mData);
         return property;
     }
     return NULL;
diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp
index 932ad6a..c7a91d6 100644
--- a/media/mtp/MtpProperty.cpp
+++ b/media/mtp/MtpProperty.cpp
@@ -120,7 +120,7 @@
     delete[] mEnumValues;
 }
 
-void MtpProperty::read(MtpDataPacket& packet, bool deviceProp) {
+void MtpProperty::read(MtpDataPacket& packet) {
 
     mCode = packet.getUInt16();
     mType = packet.getUInt16();
@@ -141,7 +141,7 @@
             break;
         default:
             readValue(packet, mDefaultValue);
-            if (deviceProp)
+            if (isDeviceProperty())
                 readValue(packet, mCurrentValue);
     }
     mGroupCode = packet.getUInt32();
@@ -159,7 +159,6 @@
     }
 }
 
-// FIXME - only works for object properties
 void MtpProperty::write(MtpDataPacket& packet) {
     packet.putUInt16(mCode);
     packet.putUInt16(mType);
diff --git a/media/mtp/MtpProperty.h b/media/mtp/MtpProperty.h
index c5b4e28..64cfb93 100644
--- a/media/mtp/MtpProperty.h
+++ b/media/mtp/MtpProperty.h
@@ -65,16 +65,22 @@
 
     inline MtpPropertyCode getPropertyCode() const { return mCode; }
 
-    void                read(MtpDataPacket& packet, bool deviceProp);
+    void                read(MtpDataPacket& packet);
     void                write(MtpDataPacket& packet);
 
     void                print();
 
+    inline bool         isDeviceProperty() const {
+                            return (   ((mCode & 0xF000) == 0x5000)
+                                    || ((mCode & 0xF800) == 0xD000));
+                        }
+
 private:
     void                readValue(MtpDataPacket& packet, MtpPropertyValue& value);
     void                writeValue(MtpDataPacket& packet, MtpPropertyValue& value);
     MtpPropertyValue*   readArrayValues(MtpDataPacket& packet, int& length);
-    void                writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, int length);
+    void                writeArrayValues(MtpDataPacket& packet,
+                                            MtpPropertyValue* values, int length);
 };
 
 }; // namespace android
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index c982114..3d3bd62 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -55,7 +55,7 @@
 //    MTP_OPERATION_SELF_TEST,
 //    MTP_OPERATION_SET_OBJECT_PROTECTION,
 //    MTP_OPERATION_POWER_DOWN,
-//    MTP_OPERATION_GET_DEVICE_PROP_DESC,
+    MTP_OPERATION_GET_DEVICE_PROP_DESC,
     MTP_OPERATION_GET_DEVICE_PROP_VALUE,
     MTP_OPERATION_SET_DEVICE_PROP_VALUE,
     MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
@@ -288,6 +288,9 @@
         case MTP_OPERATION_GET_OBJECT_PROP_DESC:
             response = doGetObjectPropDesc();
             break;
+        case MTP_OPERATION_GET_DEVICE_PROP_DESC:
+            response = doGetDevicePropDesc();
+            break;
         default:
             response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
             break;
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 379960a..57f0072 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -84,6 +84,10 @@
     return static_cast<const MotionEvent*>(motion_event)->getAction();
 }
 
+int32_t AMotionEvent_getFlags(const AInputEvent* motion_event) {
+    return static_cast<const MotionEvent*>(motion_event)->getFlags();
+}
+
 int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getMetaState();
 }
@@ -246,8 +250,8 @@
 
 
 void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
-        ALooper_callbackFunc* callback, void* data) {
-    queue->attachLooper(looper, callback, data);
+        int ident, ALooper_callbackFunc* callback, void* data) {
+    queue->attachLooper(looper, ident, callback, data);
 }
 
 void AInputQueue_detachLooper(AInputQueue* queue) {
diff --git a/native/android/looper.cpp b/native/android/looper.cpp
index 1564c47..0aeed77 100644
--- a/native/android/looper.cpp
+++ b/native/android/looper.cpp
@@ -72,9 +72,9 @@
     static_cast<PollLoop*>(looper)->decStrong((void*)ALooper_acquire);
 }
 
-void ALooper_addFd(ALooper* looper, int fd, int events,
+void ALooper_addFd(ALooper* looper, int fd, int ident, int events,
         ALooper_callbackFunc* callback, void* data) {
-    static_cast<PollLoop*>(looper)->setLooperCallback(fd, events, callback, data);
+    static_cast<PollLoop*>(looper)->setLooperCallback(fd, ident, events, callback, data);
 }
 
 int32_t ALooper_removeFd(ALooper* looper, int fd) {
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index db534e0..cf7635d 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -60,12 +60,12 @@
 }
 
 ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager,
-        ALooper* looper, ALooper_callbackFunc* callback, void* data)
+        ALooper* looper, int ident, ALooper_callbackFunc* callback, void* data)
 {
     sp<SensorEventQueue> queue =
             static_cast<SensorManager*>(manager)->createEventQueue();
     if (queue != 0) {
-        ALooper_addFd(looper, queue->getFd(), POLLIN, callback, data);
+        ALooper_addFd(looper, queue->getFd(), ident, POLLIN, callback, data);
         queue->looper = looper;
         queue->incStrong(manager);
     }
diff --git a/native/include/android/input.h b/native/include/android/input.h
index 418f609..9da122b 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -247,6 +247,22 @@
 };
 
 /*
+ * Motion event flags.
+ */
+enum {
+    /* This flag indicates that the window that received this motion event is partly
+     * or wholly obscured by another visible window above it.  This flag is set to true
+     * even if the event did not directly pass through the obscured area.
+     * A security sensitive application can check this flag to identify situations in which
+     * a malicious application may have covered up part of its content for the purpose
+     * of misleading the user or hijacking touches.  An appropriate response might be
+     * to drop the suspect touches or to take additional precautions to confirm the user's
+     * actual intent.
+     */
+    AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = 0x1,
+};
+
+/*
  * Motion event edge touch flags.
  */
 enum {
@@ -395,6 +411,9 @@
 /* Get the combined motion event action code and pointer index. */
 int32_t AMotionEvent_getAction(const AInputEvent* motion_event);
 
+/* Get the motion event flags. */
+int32_t AMotionEvent_getFlags(const AInputEvent* motion_event);
+
 /* Get the state of any meta / modifier keys that were in effect when the
  * event was generated. */
 int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event);
@@ -623,10 +642,10 @@
 
 /*
  * Add this input queue to a looper for processing.  See
- * ALooper_addFd() for information on the callback and data params.
+ * ALooper_addFd() for information on the ident, callback, and data params.
  */
 void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
-        ALooper_callbackFunc* callback, void* data);
+        int ident, ALooper_callbackFunc* callback, void* data);
 
 /*
  * Remove the input queue from the looper it is currently attached to.
diff --git a/native/include/android/looper.h b/native/include/android/looper.h
index 2917216..287bcd5 100644
--- a/native/include/android/looper.h
+++ b/native/include/android/looper.h
@@ -111,7 +111,7 @@
  *
  * Returns ALOPER_POLL_ERROR if an error occurred.
  *
- * Returns a value >= 0 containing a file descriptor if it has data
+ * Returns a value >= 0 containing an identifier if its file descriptor has data
  * and it has no callback function (requiring the caller here to handle it).
  * In this (and only this) case outEvents and outData will contain the poll
  * events and data associated with the fd.
@@ -145,10 +145,12 @@
  * descriptor was previously added, it is replaced.
  *
  * "fd" is the file descriptor to be added.
+ * "ident" is an identifier for this event, which is returned from
+ * ALooper_pollOnce().  Must be >= 0, or ALOOPER_POLL_CALLBACK if
+ * providing a non-NULL callback.
  * "events" are the poll events to wake up on.  Typically this is POLLIN.
  * "callback" is the function to call when there is an event on the file
  * descriptor.
- * "id" is an identifier to associated with this file descriptor, or 0.
  * "data" is a private data pointer to supply to the callback.
  *
  * There are two main uses of this function:
@@ -156,13 +158,13 @@
  * (1) If "callback" is non-NULL, then
  * this function will be called when there is data on the file descriptor.  It
  * should execute any events it has pending, appropriately reading from the
- * file descriptor.
+ * file descriptor.  The 'ident' is ignored in this case.
  *
- * (2) If "callback" is NULL, the fd will be returned by ALooper_pollOnce
- * when it has data available, requiring the caller to take care of processing
- * it.
+ * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce
+ * when its file descriptor has data available, requiring the caller to take
+ * care of processing it.
  */
-void ALooper_addFd(ALooper* looper, int fd, int events,
+void ALooper_addFd(ALooper* looper, int fd, int ident, int events,
         ALooper_callbackFunc* callback, void* data);
 
 /**
diff --git a/native/include/android/sensor.h b/native/include/android/sensor.h
index b4ce024..a102d43 100644
--- a/native/include/android/sensor.h
+++ b/native/include/android/sensor.h
@@ -166,7 +166,7 @@
  * Creates a new sensor event queue and associate it with a looper.
  */
 ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager,
-        ALooper* looper, ALooper_callbackFunc* callback, void* data);
+        ALooper* looper, int ident, ALooper_callbackFunc* callback, void* data);
 
 /*
  * Destroys the event queue and free all resources associated to it.
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 910e84e..cfad939 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -3,10 +3,13 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := $(call all-subdir-java-files) \
+    ../../../ex/carousel/java/com/android/ex/carousel/carousel.rs
 
 LOCAL_JAVA_LIBRARIES := services
 
+LOCAL_STATIC_JAVA_LIBRARIES := android-common-carousel
+
 LOCAL_PACKAGE_NAME := SystemUI
 LOCAL_CERTIFICATE := platform
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 18e2f479..f1f31cf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -5,6 +5,8 @@
         >
 
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
     <uses-permission android:name="android.permission.GET_TASKS" />
 
     <application
@@ -27,7 +29,7 @@
                 android:excludeFromRecents="true">
         </activity>
 
-        <activity android:name=".statusbar.RecentApplicationsActivity"
+        <activity android:name=".recent.RecentApplicationsActivity"
             android:theme="@android:style/Theme.NoTitleBar"
             android:excludeFromRecents="true"
             android:exported="true">
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 72bd776..5e48461 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -2,4 +2,5 @@
   public void notificationIconsClicked(android.view.View);
   public void systemInfoClicked(android.view.View);
   public void recentButtonClicked(android.view.View);
+  public void toggleLightsOut(android.view.View);
 }
diff --git a/packages/SystemUI/res/anim/hydraulic_brake_interpolator.xml b/packages/SystemUI/res/anim/hydraulic_brake_interpolator.xml
new file mode 100644
index 0000000..5b6778e
--- /dev/null
+++ b/packages/SystemUI/res/anim/hydraulic_brake_interpolator.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/ease_out_interpolator.xml
+**
+** Copyright 2007, 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.
+*/
+-->
+
+<decelerateInterpolator 
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:factor="10.0" />
diff --git a/packages/SystemUI/res/anim/lights_out_in.xml b/packages/SystemUI/res/anim/lights_out_in.xml
new file mode 100644
index 0000000..8154ec0
--- /dev/null
+++ b/packages/SystemUI/res/anim/lights_out_in.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <translate android:fromYDelta="100%p" android:toYDelta="0"
+        android:duration="@android:integer/config_mediumAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
+        />
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/lights_out_out.xml b/packages/SystemUI/res/anim/lights_out_out.xml
new file mode 100644
index 0000000..b4bc55a
--- /dev/null
+++ b/packages/SystemUI/res/anim/lights_out_out.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <translate android:toYDelta="100%p" android:fromYDelta="0" 
+        android:duration="@android:integer/config_mediumAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
+        />
+    <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/status_bar_in.xml b/packages/SystemUI/res/anim/status_bar_in.xml
new file mode 100644
index 0000000..460fe50
--- /dev/null
+++ b/packages/SystemUI/res/anim/status_bar_in.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <translate android:fromYDelta="-100%p" android:toYDelta="0"
+        android:duration="@android:integer/config_longAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
+        />
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/status_bar_out.xml b/packages/SystemUI/res/anim/status_bar_out.xml
new file mode 100644
index 0000000..6572ab4
--- /dev/null
+++ b/packages/SystemUI/res/anim/status_bar_out.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <translate android:toYDelta="-100%p" android:fromYDelta="0"
+        android:duration="@android:integer/config_longAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
+        />
+    <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_0.png b/packages/SystemUI/res/drawable-mdpi/battery_0.png
deleted file mode 100644
index e81638d..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_10.png b/packages/SystemUI/res/drawable-mdpi/battery_10.png
deleted file mode 100644
index a739f50..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_10.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_100.png b/packages/SystemUI/res/drawable-mdpi/battery_100.png
deleted file mode 100644
index 9a66db3..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_25.png b/packages/SystemUI/res/drawable-mdpi/battery_25.png
deleted file mode 100644
index 7ae7ebb..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_25.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_5.png b/packages/SystemUI/res/drawable-mdpi/battery_5.png
deleted file mode 100644
index 1a15a21..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_5.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_50.png b/packages/SystemUI/res/drawable-mdpi/battery_50.png
deleted file mode 100644
index 7088066..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_50.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_75.png b/packages/SystemUI/res/drawable-mdpi/battery_75.png
deleted file mode 100644
index d4cfb6f..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_75.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
deleted file mode 100644
index 60bbe6c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_off.png
new file mode 100644
index 0000000..d897ba61
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_on.png
new file mode 100644
index 0000000..0296b5b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..6c5a79b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.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
new file mode 100644
index 0000000..9ababb7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_on.png
new file mode 100644
index 0000000..668b472
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_off.png
new file mode 100644
index 0000000..e463ba4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_on.png
new file mode 100644
index 0000000..1239d50
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness.png
new file mode 100644
index 0000000..97fa5fc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_auto.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_auto.png
new file mode 100644
index 0000000..37a1533
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_auto.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_low.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_low.png
new file mode 100644
index 0000000..8a55e3a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_low.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_close.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_close.png
new file mode 100644
index 0000000..53abcbc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_close.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_default_bg.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_default_bg.png
new file mode 100644
index 0000000..3e82d4e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_default_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_off.png
new file mode 100644
index 0000000..dc2ed34
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_on.png
new file mode 100644
index 0000000..1e39fdc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_on.png
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
new file mode 100644
index 0000000..ed813dc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lightsout.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lightsout.png
new file mode 100644
index 0000000..8a07acc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lightsout.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
new file mode 100644
index 0000000..d7775f2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_open.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_open.png
new file mode 100644
index 0000000..b7d624e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_open.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_press_bg.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_press_bg.png
new file mode 100644
index 0000000..0958393
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_press_bg.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
new file mode 100644
index 0000000..178af73
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_lanscape.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_lanscape.png
new file mode 100644
index 0000000..adaadf7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_lanscape.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_portrait.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_portrait.png
new file mode 100644
index 0000000..fdc0ac7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off_portrait.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_on.png
new file mode 100644
index 0000000..4517d1b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_off.png
new file mode 100644
index 0000000..bd11e86
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_on.png
new file mode 100644
index 0000000..1b3ba2f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_on.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
new file mode 100644
index 0000000..ffbd2d3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_off.png
new file mode 100644
index 0000000..87acc14
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_on.png
new file mode 100644
index 0000000..bbb1c74
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_0.png b/packages/SystemUI/res/drawable-mdpi/signal_0.png
deleted file mode 100644
index ff2727b..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_100.png b/packages/SystemUI/res/drawable-mdpi/signal_100.png
deleted file mode 100644
index 55c34ff..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_25.png b/packages/SystemUI/res/drawable-mdpi/signal_25.png
deleted file mode 100644
index a415596..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_25.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_50.png b/packages/SystemUI/res/drawable-mdpi/signal_50.png
deleted file mode 100644
index 3c5a708..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_50.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_75.png b/packages/SystemUI/res/drawable-mdpi/signal_75.png
deleted file mode 100644
index e176f37..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_75.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan1.png b/packages/SystemUI/res/drawable-mdpi/signal_scan1.png
deleted file mode 100644
index f64b9cc..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_scan1.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan2.png b/packages/SystemUI/res/drawable-mdpi/signal_scan2.png
deleted file mode 100644
index 982feb4..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_scan2.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan3.png b/packages/SystemUI/res/drawable-mdpi/signal_scan3.png
deleted file mode 100644
index a4c0689..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_scan3.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan4.png b/packages/SystemUI/res/drawable-mdpi/signal_scan4.png
deleted file mode 100644
index d1e95fe..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_scan4.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_0.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_0.png
new file mode 100644
index 0000000..ff75a51
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_10.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_10.png
new file mode 100644
index 0000000..66ab4c6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_10.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_100.png
new file mode 100644
index 0000000..3b50500
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_20.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_20.png
new file mode 100644
index 0000000..9119065
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_30.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_30.png
new file mode 100644
index 0000000..296f19f26b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_30.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_40.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_40.png
new file mode 100644
index 0000000..9daab23
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_50.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_50.png
new file mode 100644
index 0000000..62d24c4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_60.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_60.png
new file mode 100644
index 0000000..eea927a1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_70.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_70.png
new file mode 100644
index 0000000..6816088
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_70.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_80.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_80.png
new file mode 100644
index 0000000..b7dd9bb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_90.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_90.png
new file mode 100644
index 0000000..6e36f53
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_90.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_100.png
new file mode 100644
index 0000000..8eb0f29
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_red.png b/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_red.png
new file mode 100644
index 0000000..adcc6b9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_red.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_hidenotification_handle.png b/packages/SystemUI/res/drawable-mdpi/sysbar_hidenotification_handle.png
new file mode 100644
index 0000000..e43edd7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_hidenotification_handle.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_minimeter_bg.png b/packages/SystemUI/res/drawable-mdpi/sysbar_minimeter_bg.png
new file mode 100644
index 0000000..0d265fc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_minimeter_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_panel_bg.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_panel_bg.9.png
new file mode 100644
index 0000000..77e034b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_panel_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png
new file mode 100644
index 0000000..7469372
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png
new file mode 100644
index 0000000..6625d9a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png
new file mode 100644
index 0000000..b2e763b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png
new file mode 100644
index 0000000..fb66362
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png
new file mode 100644
index 0000000..a87d94e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png
new file mode 100644
index 0000000..8e229d5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png
new file mode 100644
index 0000000..fe989d4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png
new file mode 100644
index 0000000..aac57dc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png
new file mode 100644
index 0000000..2281968
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png
new file mode 100644
index 0000000..7177ae1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png
new file mode 100644
index 0000000..7f60480
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signalmini_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signalmini_100.png
new file mode 100644
index 0000000..a5eaa63
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signalmini_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_off.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_off.9.png
new file mode 100644
index 0000000..94849d8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_off.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_on.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_on.9.png
new file mode 100644
index 0000000..f11058c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_on.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_0.png b/packages/SystemUI/res/drawable-mdpi/wifi_0.png
deleted file mode 100644
index e81638d..0000000
--- a/packages/SystemUI/res/drawable-mdpi/wifi_0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_100.png b/packages/SystemUI/res/drawable-mdpi/wifi_100.png
deleted file mode 100644
index 4468389..0000000
--- a/packages/SystemUI/res/drawable-mdpi/wifi_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_25.png b/packages/SystemUI/res/drawable-mdpi/wifi_25.png
deleted file mode 100644
index 0e389a7..0000000
--- a/packages/SystemUI/res/drawable-mdpi/wifi_25.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_50.png b/packages/SystemUI/res/drawable-mdpi/wifi_50.png
deleted file mode 100644
index 1cad0d1..0000000
--- a/packages/SystemUI/res/drawable-mdpi/wifi_50.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_75.png b/packages/SystemUI/res/drawable-mdpi/wifi_75.png
deleted file mode 100644
index dd2d7dc..0000000
--- a/packages/SystemUI/res/drawable-mdpi/wifi_75.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan1.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan1.png
deleted file mode 100644
index 0e389a7..0000000
--- a/packages/SystemUI/res/drawable-mdpi/wifi_scan1.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan2.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan2.png
deleted file mode 100644
index 34e7c70..0000000
--- a/packages/SystemUI/res/drawable-mdpi/wifi_scan2.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan3.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan3.png
deleted file mode 100644
index 4c2a9e1..0000000
--- a/packages/SystemUI/res/drawable-mdpi/wifi_scan3.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan4.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan4.png
deleted file mode 100644
index b504131..0000000
--- a/packages/SystemUI/res/drawable-mdpi/wifi_scan4.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/battery.xml b/packages/SystemUI/res/drawable/battery.xml
deleted file mode 100644
index 3bc0c74..0000000
--- a/packages/SystemUI/res/drawable/battery.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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"); 
-** 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.
-*/
--->
-
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:maxLevel="1" android:drawable="@drawable/battery_0" />
-    <item android:maxLevel="5" android:drawable="@drawable/battery_5" />
-    <item android:maxLevel="10" android:drawable="@drawable/battery_10" />
-    <item android:maxLevel="25" android:drawable="@drawable/battery_25" />
-    <item android:maxLevel="50" android:drawable="@drawable/battery_50" />
-    <item android:maxLevel="75" android:drawable="@drawable/battery_75" />
-    <item android:maxLevel="101" android:drawable="@drawable/battery_100" />
-</level-list>
-
diff --git a/packages/SystemUI/res/drawable/battery_charging.xml b/packages/SystemUI/res/drawable/battery_charging.xml
deleted file mode 100644
index c9b77dd..0000000
--- a/packages/SystemUI/res/drawable/battery_charging.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?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"); 
-** 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.
-*/
--->
-
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:maxLevel="25">
-        <animation-list android:oneshot="false">
-            <item android:drawable="@drawable/battery_0" android:duration="1000" />
-            <item android:drawable="@drawable/battery_25" android:duration="1000" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="50">
-        <animation-list android:oneshot="false">
-            <item android:drawable="@drawable/battery_25" android:duration="1000" />
-            <item android:drawable="@drawable/battery_50" android:duration="1000" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="75">
-        <animation-list android:oneshot="false">
-            <item android:drawable="@drawable/battery_50" android:duration="1000" />
-            <item android:drawable="@drawable/battery_75" android:duration="1000" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="92">
-        <animation-list android:oneshot="false">
-            <item android:drawable="@drawable/battery_75" android:duration="1000" />
-            <item android:drawable="@drawable/battery_100" android:duration="1000" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="101" android:drawable="@drawable/battery_100" />
-</level-list>
-
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_icon_bg.xml b/packages/SystemUI/res/drawable/ic_sysbar_icon_bg.xml
new file mode 100644
index 0000000..d8ba2a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_icon_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<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>
+
diff --git a/packages/SystemUI/res/drawable/signal.xml b/packages/SystemUI/res/drawable/signal.xml
deleted file mode 100644
index 7367418..0000000
--- a/packages/SystemUI/res/drawable/signal.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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"); 
-** 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.
-*/
--->
-
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:maxLevel="01" android:drawable="@drawable/signal_0" />
-    <item android:maxLevel="25" android:drawable="@drawable/signal_25" />
-    <item android:maxLevel="50" android:drawable="@drawable/signal_50" />
-    <item android:maxLevel="75" android:drawable="@drawable/signal_75" />
-    <item android:maxLevel="101" android:drawable="@drawable/signal_100" />
-</level-list>
-
diff --git a/packages/SystemUI/res/drawable/signal_scan.xml b/packages/SystemUI/res/drawable/signal_scan.xml
deleted file mode 100644
index fe21992..0000000
--- a/packages/SystemUI/res/drawable/signal_scan.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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"); 
-** 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.
-*/
--->
-
-<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/signal_scan1" android:duration="500" />
-    <item android:drawable="@drawable/signal_scan2" android:duration="500" />
-    <item android:drawable="@drawable/signal_scan3" android:duration="500" />
-    <item android:drawable="@drawable/signal_scan4" android:duration="500" />
-    <item android:drawable="@drawable/signal_scan3" android:duration="500" />
-    <item android:drawable="@drawable/signal_scan2" android:duration="500" />
-</animation-list>
-
diff --git a/packages/SystemUI/res/drawable/sysbar_battery.xml b/packages/SystemUI/res/drawable/sysbar_battery.xml
new file mode 100644
index 0000000..9551bf0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sysbar_battery.xml
@@ -0,0 +1,33 @@
+<?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"); 
+** 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.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:maxLevel="01" android:drawable="@drawable/sysbar_battery_0" />
+    <item android:maxLevel="10" android:drawable="@drawable/sysbar_battery_10" />
+    <item android:maxLevel="20" android:drawable="@drawable/sysbar_battery_20" />
+    <item android:maxLevel="30" android:drawable="@drawable/sysbar_battery_30" />
+    <item android:maxLevel="40" android:drawable="@drawable/sysbar_battery_40" />
+    <item android:maxLevel="50" android:drawable="@drawable/sysbar_battery_50" />
+    <item android:maxLevel="60" android:drawable="@drawable/sysbar_battery_60" />
+    <item android:maxLevel="70" android:drawable="@drawable/sysbar_battery_70" />
+    <item android:maxLevel="80" android:drawable="@drawable/sysbar_battery_80" />
+    <item android:maxLevel="90" android:drawable="@drawable/sysbar_battery_90" />
+    <item android:maxLevel="101" android:drawable="@drawable/sysbar_battery_100" />
+</level-list>
diff --git a/packages/SystemUI/res/drawable/wifi.xml b/packages/SystemUI/res/drawable/sysbar_batterymini.xml
similarity index 61%
copy from packages/SystemUI/res/drawable/wifi.xml
copy to packages/SystemUI/res/drawable/sysbar_batterymini.xml
index 6e63af6..c7300e6 100644
--- a/packages/SystemUI/res/drawable/wifi.xml
+++ b/packages/SystemUI/res/drawable/sysbar_batterymini.xml
@@ -18,12 +18,12 @@
 */
 -->
 
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:maxLevel="01" android:drawable="@drawable/wifi_0" />
-    <item android:maxLevel="25" android:drawable="@drawable/wifi_25" />
-    <item android:maxLevel="50" android:drawable="@drawable/wifi_50" />
-    <item android:maxLevel="75" android:drawable="@drawable/wifi_75" />
-    <item android:maxLevel="101" android:drawable="@drawable/wifi_100" />
-
-</level-list>
-
+<clip xmlns:android="http://schemas.android.com/apk/res/android"
+        android:clipOrientation="horizontal"
+        android:maxLevel="100"
+        android:gravity="left">
+    <level-list>
+        <item android:maxLevel="1500"  android:drawable="@drawable/sysbar_batterymini_red" />
+        <item android:maxLevel="10000" android:drawable="@drawable/sysbar_batterymini_100" />
+    </level-list>
+</clip>
diff --git a/packages/SystemUI/res/drawable/sysbar_signal.xml b/packages/SystemUI/res/drawable/sysbar_signal.xml
new file mode 100644
index 0000000..9561c37
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sysbar_signal.xml
@@ -0,0 +1,33 @@
+<?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"); 
+** 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.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:maxLevel="01" android:drawable="@drawable/sysbar_signal_0" />
+    <item android:maxLevel="10" android:drawable="@drawable/sysbar_signal_10" />
+    <item android:maxLevel="20" android:drawable="@drawable/sysbar_signal_20" />
+    <item android:maxLevel="30" android:drawable="@drawable/sysbar_signal_30" />
+    <item android:maxLevel="40" android:drawable="@drawable/sysbar_signal_40" />
+    <item android:maxLevel="50" android:drawable="@drawable/sysbar_signal_50" />
+    <item android:maxLevel="60" android:drawable="@drawable/sysbar_signal_60" />
+    <item android:maxLevel="70" android:drawable="@drawable/sysbar_signal_70" />
+    <item android:maxLevel="80" android:drawable="@drawable/sysbar_signal_80" />
+    <item android:maxLevel="90" android:drawable="@drawable/sysbar_signal_90" />
+    <item android:maxLevel="101" android:drawable="@drawable/sysbar_signal_100" />
+</level-list>
diff --git a/packages/SystemUI/res/drawable/wifi.xml b/packages/SystemUI/res/drawable/sysbar_signalmini.xml
similarity index 61%
copy from packages/SystemUI/res/drawable/wifi.xml
copy to packages/SystemUI/res/drawable/sysbar_signalmini.xml
index 6e63af6..598bf10 100644
--- a/packages/SystemUI/res/drawable/wifi.xml
+++ b/packages/SystemUI/res/drawable/sysbar_signalmini.xml
@@ -18,12 +18,8 @@
 */
 -->
 
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:maxLevel="01" android:drawable="@drawable/wifi_0" />
-    <item android:maxLevel="25" android:drawable="@drawable/wifi_25" />
-    <item android:maxLevel="50" android:drawable="@drawable/wifi_50" />
-    <item android:maxLevel="75" android:drawable="@drawable/wifi_75" />
-    <item android:maxLevel="101" android:drawable="@drawable/wifi_100" />
-
-</level-list>
-
+<clip xmlns:android="http://schemas.android.com/apk/res/android"
+        android:clipOrientation="horizontal"
+        android:gravity="right"
+        android:maxLevel="10000"
+        android:drawable="@drawable/sysbar_signalmini_100" />
diff --git a/packages/SystemUI/res/drawable/sysbar_wifi.xml b/packages/SystemUI/res/drawable/sysbar_wifi.xml
new file mode 100644
index 0000000..9561c37
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sysbar_wifi.xml
@@ -0,0 +1,33 @@
+<?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"); 
+** 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.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:maxLevel="01" android:drawable="@drawable/sysbar_signal_0" />
+    <item android:maxLevel="10" android:drawable="@drawable/sysbar_signal_10" />
+    <item android:maxLevel="20" android:drawable="@drawable/sysbar_signal_20" />
+    <item android:maxLevel="30" android:drawable="@drawable/sysbar_signal_30" />
+    <item android:maxLevel="40" android:drawable="@drawable/sysbar_signal_40" />
+    <item android:maxLevel="50" android:drawable="@drawable/sysbar_signal_50" />
+    <item android:maxLevel="60" android:drawable="@drawable/sysbar_signal_60" />
+    <item android:maxLevel="70" android:drawable="@drawable/sysbar_signal_70" />
+    <item android:maxLevel="80" android:drawable="@drawable/sysbar_signal_80" />
+    <item android:maxLevel="90" android:drawable="@drawable/sysbar_signal_90" />
+    <item android:maxLevel="101" android:drawable="@drawable/sysbar_signal_100" />
+</level-list>
diff --git a/packages/SystemUI/res/drawable/wifi.xml b/packages/SystemUI/res/drawable/sysbar_wifimini.xml
similarity index 62%
rename from packages/SystemUI/res/drawable/wifi.xml
rename to packages/SystemUI/res/drawable/sysbar_wifimini.xml
index 6e63af6..ca6c9ed 100644
--- a/packages/SystemUI/res/drawable/wifi.xml
+++ b/packages/SystemUI/res/drawable/sysbar_wifimini.xml
@@ -18,12 +18,8 @@
 */
 -->
 
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:maxLevel="01" android:drawable="@drawable/wifi_0" />
-    <item android:maxLevel="25" android:drawable="@drawable/wifi_25" />
-    <item android:maxLevel="50" android:drawable="@drawable/wifi_50" />
-    <item android:maxLevel="75" android:drawable="@drawable/wifi_75" />
-    <item android:maxLevel="101" android:drawable="@drawable/wifi_100" />
-
-</level-list>
-
+<clip xmlns:android="http://schemas.android.com/apk/res/android"
+        android:clipOrientation="horizontal"
+        android:gravity="right"
+        android:maxLevel="100"
+        android:drawable="@drawable/sysbar_signalmini_100" />
diff --git a/packages/SystemUI/res/drawable/wifi_scan.xml b/packages/SystemUI/res/drawable/wifi_scan.xml
deleted file mode 100644
index 5888d0e..0000000
--- a/packages/SystemUI/res/drawable/wifi_scan.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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"); 
-** 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.
-*/
--->
-
-<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/wifi_scan1" android:duration="500" />
-    <item android:drawable="@drawable/wifi_scan2" android:duration="500" />
-    <item android:drawable="@drawable/wifi_scan3" android:duration="500" />
-    <item android:drawable="@drawable/wifi_scan4" android:duration="500" />
-    <item android:drawable="@drawable/wifi_scan3" android:duration="500" />
-    <item android:drawable="@drawable/wifi_scan2" android:duration="500" />
-</animation-list>
-
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index a0c2c95..f4040d91 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -19,13 +19,16 @@
 -->
 
 <!--    android:background="@drawable/status_bar_closed_default_background" -->
-<RelativeLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:background="@drawable/status_bar_background"
-    android:focusable="true"
-    android:descendantFocusability="afterDescendants"
     >
+    <RelativeLayout
+        android:id="@+id/bar_contents"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        >
 
         <com.android.systemui.statusbar.tablet.NotificationIconArea
             android:id="@+id/notificationIcons"
@@ -35,18 +38,7 @@
             android:paddingLeft="6dip"
             android:gravity="center_vertical"
             android:orientation="horizontal"
-            android:clickable="true"
-            android:onClick="notificationIconsClicked"
-            android:background="@drawable/status_bar_icon_tray"
             >
-            <ImageView
-                class="com.android.systemui.statusbar.tablet.NotificationIconArea$MoreView"
-                android:id="@+id/expand"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:src="@drawable/status_bar_expand"
-                android:onClick="notificationIconsClicked"
-                />
             <view
                 class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
                 android:id="@+id/icons"
@@ -57,10 +49,19 @@
             <view
                 class="com.android.systemui.statusbar.tablet.NotificationIconArea$DraggerView"
                 android:id="@+id/handle"
-                android:layout_width="24dip"
+                android:layout_width="32dip"
                 android:layout_height="match_parent"
+                android:background="@drawable/sysbar_hidenotification_handle"
                 android:layout_marginLeft="8dip"
                 />
+            <ImageView
+                class="com.android.systemui.statusbar.tablet.NotificationIconArea$MoreView"
+                android:id="@+id/expand"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:src="@drawable/ic_sysbar_open"
+                android:onClick="notificationIconsClicked"
+                />
 
         </com.android.systemui.statusbar.tablet.NotificationIconArea>
 
@@ -101,34 +102,27 @@
         </LinearLayout>
 
         <include layout="@layout/status_bar_center"
-            android:layout_width="160dip"
+            android:layout_width="256dip"
             android:layout_height="match_parent"
             android:layout_centerInParent="true"
             />
 
-        <com.android.systemui.statusbar.KeyButtonView android:id="@+id/back"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_toLeftOf="@+id/menu"
-            android:paddingLeft="4dip"
-            android:paddingRight="4dip"
-            android:src="@drawable/status_bar_back"
-            systemui:keyCode="4"
-            />
         <com.android.systemui.statusbar.KeyButtonView android:id="@+id/menu"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:layout_toLeftOf="@+id/recent"
-            android:src="@drawable/status_bar_menu"
+            android:src="@drawable/ic_sysbar_menu"
+            android:background="@drawable/ic_sysbar_icon_bg"
             android:paddingLeft="4dip"
             android:paddingRight="4dip"
             systemui:keyCode="82"
             />
-        <Button android:id="@+id/recent"
+        <ImageButton android:id="@+id/recent"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:layout_toLeftOf="@+id/home"
-            android:background="@drawable/status_bar_recent"
+            android:src="@drawable/ic_sysbar_recent"
+            android:background="@drawable/ic_sysbar_icon_bg"
             android:paddingLeft="4dip"
             android:paddingRight="4dip"
             android:onClick="recentButtonClicked"
@@ -136,11 +130,36 @@
         <com.android.systemui.statusbar.KeyButtonView android:id="@+id/home"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
+            android:layout_toLeftOf="@+id/back"
+            android:paddingLeft="4dip"
+            android:paddingRight="4dip"
+            android:src="@drawable/ic_sysbar_home"
+            android:background="@drawable/ic_sysbar_icon_bg"
+            systemui:keyCode="3"
+            />
+        <com.android.systemui.statusbar.KeyButtonView android:id="@+id/back"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
             android:layout_alignParentRight="true"
             android:paddingLeft="4dip"
             android:paddingRight="4dip"
-            android:src="@drawable/status_bar_home"
-            systemui:keyCode="3"
+            android:src="@drawable/ic_sysbar_back"
+            android:background="@drawable/ic_sysbar_icon_bg"
+            systemui:keyCode="4"
             />
-</RelativeLayout>
+    </RelativeLayout>
+
+    <!-- It's curtains for you. -->
+    <ImageView
+        android:id="@+id/lights_out"
+        android:src="@drawable/ic_sysbar_lightsout"
+        android:gravity="center"
+        android:background="#FF000000"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="invisible"
+        android:clickable="true"
+        android:onClick="toggleLightsOut"
+        />
+</FrameLayout>
 
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
index 5bf8316..2d74672 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
@@ -18,7 +18,7 @@
 <RelativeLayout android:id="@+id/systemInfo"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
-    android:layout_width="160dip"
+    android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_centerInParent="true"
     android:clickable="true"
@@ -38,17 +38,35 @@
         />
     <ImageView
         android:id="@+id/battery"
-        android:layout_width="48dip"
-        android:layout_height="48dip"
+        android:layout_width="64dip"
+        android:layout_height="16dip"
         android:layout_toLeftOf="@id/clock"
-        android:src="@drawable/battery"
+        android:layout_centerInParent="true"
+        android:background="@drawable/sysbar_minimeter_bg" 
         />
     <ImageView
         android:id="@+id/signal"
-        android:layout_width="48dip"
-        android:layout_height="48dip"
+        android:layout_width="64dip"
+        android:layout_height="16dip"
         android:layout_toRightOf="@id/clock"
-        android:src="@drawable/signal"
+        android:layout_centerInParent="true"
+        android:background="@drawable/sysbar_minimeter_bg" 
+        />
+    <ImageView
+        android:id="@+id/battery_icon"
+        android:layout_height="30dip"
+        android:layout_width="30dip"
+        android:layout_toLeftOf="@id/battery"
+        android:layout_centerInParent="true"
+        android:src="@drawable/ic_sysbar_battery_mini"
+        />
+    <ImageView
+        android:id="@+id/signal_icon"
+        android:layout_height="30dip"
+        android:layout_width="30dip"
+        android:layout_toRightOf="@id/signal"
+        android:layout_centerInParent="true"
+        android:src="@drawable/ic_sysbar_wifi_mini"
         />
 </RelativeLayout>
 
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
index 36c6388..7e469f7 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
@@ -22,130 +22,187 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
-    android:background="#FF000000"
+    android:background="@drawable/sysbar_panel_bg"
     android:orientation="vertical"
+    android:paddingLeft="70dip"
+    android:paddingRight="120dip"
     >
 
-    <TextView android:id="@+id/settings_button"
-        style="?android:attr/textAppearance"
+    <!-- top row: quick settings buttons -->
+    <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="left|center_vertical"
-        android:layout_marginTop="2dip"
-        android:layout_marginBottom="1dip"
-        android:layout_marginRight="10dip"
-        android:padding="8dip"
-        android:textSize="20sp"
-        android:text="@string/system_panel_settings_button"
-        />
+        android:layout_height="64dip"
+        android:orientation="horizontal"
+        android:gravity="center"
+        >
+        <ImageButton android:id="@+id/brightness"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:src="@drawable/ic_sysbar_brightness"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+        <ImageButton android:id="@+id/sound"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_sound_on"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+        <ImageButton android:id="@+id/orientation"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_rotate_on"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+        <ImageButton android:id="@+id/airplane"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_airplane_on"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+        <ImageButton android:id="@+id/gps"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_gps_on"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+        <ImageButton android:id="@+id/bluetooth"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_bluetooth_on"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+    </LinearLayout>
 
+    <!-- main row: meters, clock -->
+    <RelativeLayout
+        android:padding="8dip"
+        android:layout_width="match_parent"
+        android:layout_height="192dip"
+        >
+        <RelativeLayout
+            android:layout_width="256dip"
+            android:layout_height="192dip"
+            android:layout_alignParentLeft="true"
+            android:layout_marginLeft="48dip"
+            >
+            <ImageView android:id="@+id/battery_meter"
+                android:layout_width="256dip"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:scaleType="centerCrop"
+                android:src="@drawable/sysbar_battery"
+                />
+            <TextView android:id="@+id/battery_info"
+                style="@style/TextAppearance.StatusBar.SystemPanel"
+                android:layout_width="match_parent"
+                android:layout_height="24dip"
+                android:gravity="center"
+                android:layout_above="@id/battery_meter"
+                />
+        </RelativeLayout>
+
+        <com.android.systemui.statusbar.Clock
+            style="@style/TextAppearance.StatusBar.SystemPanel"
+            android:id="@+id/clock"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textSize="50sp"
+            android:textStyle="normal"
+            android:textColor="#FFFFFFFF"
+            android:layout_centerHorizontal="true"
+            android:layout_alignParentBottom="true"
+            />
+
+        <RelativeLayout
+            android:layout_width="256dip"
+            android:layout_height="192dip"
+            android:layout_alignParentRight="true"
+            android:layout_marginRight="48dip"
+            >
+            <ImageView android:id="@+id/signal_meter"
+                android:layout_width="256dip"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:scaleType="centerCrop"
+                android:src="@drawable/sysbar_signal"
+                />
+
+            <TextView android:id="@+id/signal_info"
+                style="@style/TextAppearance.StatusBar.SystemPanel"
+                android:layout_width="match_parent"
+                android:layout_height="24dip"
+                android:gravity="center"
+                android:layout_above="@id/signal_meter"
+                />
+        </RelativeLayout>
+
+        <ImageView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="8dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_battery_on"
+            />
+        <ImageView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="8dip"
+            android:layout_marginRight="8dip"
+            android:src="@drawable/ic_sysbar_wifi_on"
+            />
+    </RelativeLayout>
+
+    <!-- bottom row: transient indicators, settings button -->
     <View
         android:layout_width="match_parent"
         android:layout_height="1sp"
         android:background="@android:drawable/divider_horizontal_dark"
         />
-
-    <LinearLayout
-        android:padding="8dip"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:gravity="center"
-        >
-        <ImageButton android:id="@+id/brightness"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:src="@drawable/system_panel_brightness_default"
-            android:background="@drawable/button_frame"
-            />
-        <ImageButton android:id="@+id/sound"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="8dip"
-            android:src="@drawable/system_panel_sound_default"
-            android:background="@drawable/button_frame"
-            />
-        <ImageButton android:id="@+id/orientation"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="8dip"
-            android:src="@drawable/system_panel_orientation_default"
-            android:background="@drawable/button_frame"
-            />
-        <ImageButton android:id="@+id/airplane"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="8dip"
-            android:src="@drawable/system_panel_airplane_default"
-            android:background="@drawable/button_frame"
-            />
-    </LinearLayout>
-
     <RelativeLayout
-        android:padding="8dip"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="48dip"
         >
-        <RelativeLayout
-            android:layout_width="120dip"
+        <LinearLayout
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentLeft="true"
+            android:orientation="horizontal"
             >
-            <ImageView android:id="@+id/battery_meter"
-                android:layout_width="96dip"
+            <!-- TODO: alarm -->
+            <!-- TODO: sync -->
+            <TextView android:id="@+id/date"
+                android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_centerHorizontal="true"
-                android:scaleType="centerCrop"
-                android:src="@drawable/battery"
                 />
+        </LinearLayout>
 
-            <TextView android:id="@+id/battery_info"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:gravity="center"
-                android:layout_below="@id/battery_meter"
-                />
-        </RelativeLayout>
-
-        <com.android.systemui.statusbar.Clock
-            style="@*android:style/TextAppearance.StatusBar.Icon"
-            android:id="@+id/clock"
+        <TextView android:id="@+id/settings_button"
+            style="@style/TextAppearance.StatusBar.TextButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:textSize="20sp"
-            android:textStyle="bold"
-            android:padding="2dip"
-            android:layout_centerHorizontal="true"
-            />
-
-        <TextView android:id="@+id/date"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/clock"
-            />
-
-        <RelativeLayout
-            android:layout_width="120dip"
-            android:layout_height="wrap_content"
             android:layout_alignParentRight="true"
-            >
-            <ImageView android:id="@+id/signal_meter"
-                android:layout_width="96dip"
-                android:layout_height="wrap_content"
-                android:layout_centerHorizontal="true"
-                android:scaleType="centerCrop"
-                android:src="@drawable/signal"
-                />
-
-            <TextView android:id="@+id/signal_info"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:gravity="center"
-                android:layout_below="@id/signal_meter"
-                />
-        </RelativeLayout>
+            android:layout_centerVertical="true"
+            android:paddingRight="32dip"
+            android:paddingLeft="32dip"
+            android:textSize="20sp"
+            android:text="@string/system_panel_settings_button"
+            />
+        <View
+            android:layout_height="match_parent"
+            android:layout_width="1sp"
+            android:layout_toLeftOf="@id/settings_button"
+            android:background="@*android:drawable/divider_vertical_dark"
+            />
 
     </RelativeLayout>
-
 </com.android.systemui.statusbar.tablet.SystemPanel>
diff --git a/core/res/res/layout/recent_apps_activity.xml b/packages/SystemUI/res/layout/recent_apps_activity.xml
similarity index 88%
rename from core/res/res/layout/recent_apps_activity.xml
rename to packages/SystemUI/res/layout/recent_apps_activity.xml
index d962339..ec661e8 100644
--- a/core/res/res/layout/recent_apps_activity.xml
+++ b/packages/SystemUI/res/layout/recent_apps_activity.xml
@@ -32,7 +32,7 @@
         android:textColor="#80FFFFFF"
         android:textStyle="bold"
         android:singleLine="true"
-        android:text="@android:string/recent_tasks_title"
+        android:text="@string/recent_tasks_title"
         android:visibility="gone"/>
 
     <!-- This is only intended to be visible when carousel is invisible -->
@@ -42,14 +42,14 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center"
         android:textAppearance="?android:attr/textAppearanceSmall"
-        android:text="@android:string/no_recent_tasks"
+        android:text="@string/recent_tasks_empty"
         android:visibility="gone"/>
 
-    <com.android.internal.widget.CarouselView
+    <com.android.systemui.recent.RecentApplicationsCarouselView
         android:id="@+id/carousel"
         android:layout_width="match_parent"
         android:layout_height="0dip"
         android:layout_weight="1">
-    </com.android.internal.widget.CarouselView>
+    </com.android.systemui.recent.RecentApplicationsCarouselView>
 
 </LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2df3b6d..037dc4a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -54,4 +54,17 @@
     <!-- Name of the button that links to the Settings app. [MAXCHARS=NONE] -->
     <string name="system_panel_settings_button">Settings</string>
 
+
+    <!-- Recent Tasks dialog: title
+     TODO: this should move to SystemUI.apk, but the code for the old 
+            recent dialog is still in the framework
+     -->
+    <string name="recent_tasks_title">Recent</string>
+    <!-- Recent Tasks dialog: message when there are no recent applications
+     TODO: this should move to SystemUI.apk, but the code for the old 
+            recent dialog is still in the framework
+     -->
+    <string name="recent_tasks_empty">No recent applications.</string>
+
+    
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 816f34a4..f592703 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -25,4 +25,18 @@
     <style name="TextAppearance.StatusBar.IntruderAlert"
         parent="@android:style/TextAppearance.StatusBar">
     </style>
+
+    <style name="TextAppearance.StatusBar.SystemPanel"
+        parent="@android:style/TextAppearance.StatusBar">
+        <item name="android:textAppearance">?android:attr/textAppearance</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#50FFFFFF</item>
+    </style>
+
+    <style name="TextAppearance.StatusBar.TextButton"
+        parent="@android:style/TextAppearance.StatusBar">
+        <item name="android:textAppearance">?android:attr/textAppearance</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#FFFFFFFF</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/RecentApplicationsActivity.java
rename to packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
index a5ba7e2..6838dda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -15,16 +15,16 @@
  */
 
 
-package com.android.systemui.statusbar;
+package com.android.systemui.recent;
 
-import com.android.internal.R;
+import com.android.systemui.R;
+
+import com.android.ex.carousel.CarouselView;
+import com.android.ex.carousel.CarouselRS.CarouselCallback;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import com.android.internal.widget.CarouselView;
-import com.android.internal.widget.CarouselRS.CarouselCallback;
-
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.IThumbnailReceiver;
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java
new file mode 100644
index 0000000..1c8ec95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.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 com.android.systemui.recent;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.ex.carousel.CarouselView;
+import com.android.systemui.R;
+
+public class RecentApplicationsCarouselView extends CarouselView {
+
+    public RecentApplicationsCarouselView(Context context) {
+        this(context, null);
+    }
+
+    public RecentApplicationsCarouselView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public Info getRenderScriptInfo() {
+        return new Info(R.raw.carousel);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java
index b0508b8..fe29dea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java
@@ -25,10 +25,12 @@
 import android.os.ServiceManager;
 import android.util.AttributeSet;
 import android.util.Slog;
+import android.view.HapticFeedbackConstants;
 import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 import android.widget.ImageView;
 import android.widget.RemoteViews.RemoteView;
 
@@ -37,9 +39,22 @@
 public class KeyButtonView extends ImageView {
     IWindowManager mWindowManager;
     long mDownTime;
-    boolean mSending;
+    boolean mSending, mLongPressed;
     int mCode;
     int mRepeat;
+    Runnable mCheckLongPress = new Runnable() {
+        public void run() {
+            Slog.d("KeyButtonView", "longpress");
+            if (isPressed()) {
+                mLongPressed = true;
+                mRepeat++;
+                sendEvent(KeyEvent.ACTION_DOWN,
+                        KeyEvent.FLAG_FROM_SYSTEM
+                        | KeyEvent.FLAG_VIRTUAL_HARD_KEY
+                        | KeyEvent.FLAG_LONG_PRESS);
+            }
+        }
+    };
 
     public KeyButtonView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -69,12 +84,16 @@
 
         switch (action) {
             case MotionEvent.ACTION_DOWN:
+                Slog.d("KeyButtonView", "press");
                 mDownTime = SystemClock.uptimeMillis();
                 mRepeat = 0;
                 mSending = true;
+                mLongPressed = false;
                 sendEvent(KeyEvent.ACTION_DOWN,
-                        KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_SOFT_KEYBOARD, mDownTime);
+                        KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, mDownTime);
                 setPressed(true);
+                removeCallbacks(mCheckLongPress);
+                postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
                 break;
             case MotionEvent.ACTION_MOVE:
                 if (mSending) {
@@ -83,19 +102,21 @@
                     if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) {
                         mSending = false;
                         sendEvent(KeyEvent.ACTION_UP,
-                                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_SOFT_KEYBOARD
+                                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY
                                         | KeyEvent.FLAG_CANCELED);
                         setPressed(false);
+                        removeCallbacks(mCheckLongPress);
                     }
                 }
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                if (mSending) {
+                setPressed(false);
+                if (mSending && !mLongPressed) {
                     mSending = false;
                     sendEvent(KeyEvent.ACTION_UP,
-                            KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_SOFT_KEYBOARD);
-                    setPressed(false);
+                            KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
+                    removeCallbacks(mCheckLongPress);
                 }
                 break;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
index 33acecb..48243ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -39,6 +39,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.util.Log;
 import android.view.Display;
@@ -468,7 +469,11 @@
         }
 
         // Restart the ticker if it's still running
-        tick(notification);
+        if (notification.notification.tickerText != null
+                && !TextUtils.equals(notification.notification.tickerText,
+                    oldEntry.notification.notification.tickerText)) {
+            tick(notification);
+        }
 
         // Recalculate the position of the sliding windows and the titles.
         setAreThereNotifications();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
index a32b01f..491ef1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
@@ -20,6 +20,7 @@
 import android.app.PendingIntent;
 import android.app.Service;
 import android.app.StatusBarManager;
+import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -28,6 +29,7 @@
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.location.LocationManager;
 import android.media.AudioManager;
 import android.net.NetworkInfo;
 import android.net.wifi.SupplicantState;
@@ -84,7 +86,6 @@
     private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
     private static final int DEFAULT_BACKLIGHT = (int) (android.os.Power.BRIGHTNESS_ON * 0.4f);
 
-
     private TabletStatusBarService mBar;
     private boolean mAirplaneMode;
 
@@ -92,6 +93,8 @@
     private ImageButton mSoundButton;
     private ImageButton mOrientationButton;
     private ImageButton mAirplaneButton;
+    private ImageButton mGpsButton;
+    private ImageButton mBluetoothButton;
 
     private ImageView mBatteryMeter;
     private ImageView mSignalMeter;
@@ -102,6 +105,7 @@
     private final AudioManager mAudioManager;
     private final WifiManager mWifiManager;
     private final TelephonyManager mPhone;
+    private final BluetoothAdapter mBluetoothAdapter;
 
     // state trackers for telephony code
     IccCard.State mSimState = IccCard.State.READY;
@@ -123,7 +127,7 @@
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
             if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
-                mSoundButton.setAlpha(getSilentMode() ? 0x7F : 0xFF);
+                refreshSound();
             } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
                 updateBattery(intent);
             } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)
@@ -133,6 +137,8 @@
                 updateWifiState(intent);
             } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
                 updateSimState(intent);
+            } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+                refreshBluetooth();
             }
         }
     };
@@ -295,7 +301,7 @@
             mWifiConnected = detailedState == NetworkInfo.DetailedState.CONNECTED;
         } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
             final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
-            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 6) * 20;
+            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 101);
             mWifiLevel = mWifiConnected ? newSignalLevel : 0;
         }
 
@@ -351,7 +357,7 @@
             level = 0;
         }
 
-        mSignalMeter.setImageResource(mWifiConnected ? R.drawable.wifi : R.drawable.signal);
+        mSignalMeter.setImageResource(R.drawable.sysbar_signal);
         mSignalMeter.setImageLevel(level);
         mSignalText.setText(text);
 
@@ -367,7 +373,7 @@
         final int level = intent.getIntExtra("level", 0);
         final boolean plugged = intent.getIntExtra("plugged", 0) != 0;
 
-        mBatteryMeter.setImageResource(plugged ? R.drawable.battery_charging : R.drawable.battery);
+        mBatteryMeter.setImageResource(R.drawable.sysbar_battery);
         mBatteryMeter.setImageLevel(level);
         mBatteryText.setText(getContext()
                 .getString(R.string.system_panel_battery_meter_format, level));
@@ -401,6 +407,9 @@
 
         // mobile data 
         mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+
+        // Bluetooth
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
     public void onAttachedToWindow() {
@@ -445,6 +454,22 @@
             }
         });
 
+        mGpsButton = (ImageButton)findViewById(R.id.gps);
+        mGpsButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                toggleGps();
+                refreshGps();
+            }
+        });
+
+        mBluetoothButton = (ImageButton)findViewById(R.id.bluetooth);
+        mBluetoothButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                toggleBluetooth();
+                refreshBluetooth();
+            }
+        });
+
         // register for broadcasts
         IntentFilter filter = new IntentFilter();
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
@@ -454,19 +479,22 @@
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
         getContext().registerReceiver(mReceiver, filter);
         
         mBatteryMeter = (ImageView)findViewById(R.id.battery_meter);
-        mBatteryMeter.setImageResource(R.drawable.battery);
+        mBatteryMeter.setImageResource(R.drawable.sysbar_battery);
         mBatteryMeter.setImageLevel(0);
         mSignalMeter = (ImageView)findViewById(R.id.signal_meter);
-        mBatteryMeter.setImageResource(R.drawable.signal);
+        mBatteryMeter.setImageResource(R.drawable.sysbar_signal);
         mBatteryMeter.setImageLevel(0);
 
         mBatteryText = (TextView)findViewById(R.id.battery_info);
         mSignalText = (TextView)findViewById(R.id.signal_info);
 
         refreshSignalMeters();
+        refreshBluetooth();
+        refreshGps();
     }
 
     public void onDetachedFromWindow() {
@@ -492,7 +520,8 @@
 //    }
 
     private void rotateBrightness() {
-        int alpha = 0xFF;
+        int icon = R.drawable.ic_sysbar_brightness;
+        int bg = R.drawable.sysbar_toggle_bg_on;
         Context context = getContext();
         try {
             IPowerManager power = IPowerManager.Stub.asInterface(
@@ -513,18 +542,16 @@
                 // Technically, not a toggle...
                 if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
                     brightness = MINIMUM_BACKLIGHT;
+                    icon = R.drawable.ic_sysbar_brightness_low;
                     brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
-                    alpha = 0x40;
                 } else if (brightness < DEFAULT_BACKLIGHT) {
                     brightness = DEFAULT_BACKLIGHT;
-                    alpha = 0xC0;
                 } else if (brightness < MAXIMUM_BACKLIGHT) {
                     brightness = MAXIMUM_BACKLIGHT;
-                    alpha = 0xFF;
                 } else {
                     brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
                     brightness = MINIMUM_BACKLIGHT;
-                    alpha = 0x60;
+                    icon = R.drawable.ic_sysbar_brightness_auto;
                 }
 
                 if (context.getResources().getBoolean(
@@ -546,7 +573,8 @@
         } catch (Settings.SettingNotFoundException e) {
         }
 
-        mBrightnessButton.setAlpha(alpha);
+        mBrightnessButton.setImageResource(icon);
+        mBrightnessButton.setBackgroundResource(bg);
     }
 
     PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@@ -558,7 +586,12 @@
             mServiceState = serviceState;
             mAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
             if (mAirplaneButton != null) {
-                mAirplaneButton.setAlpha(mAirplaneMode ? 0xFF : 0x7F);
+                mAirplaneButton.setImageResource(mAirplaneMode 
+                                                 ? R.drawable.ic_sysbar_airplane_on
+                                                 : R.drawable.ic_sysbar_airplane_off);
+                mAirplaneButton.setBackgroundResource(mAirplaneMode 
+                                                 ? R.drawable.sysbar_toggle_bg_on
+                                                 : R.drawable.sysbar_toggle_bg_off);
             }
             updateDataState();
         }
@@ -621,4 +654,51 @@
         }
     }
 
+    void refreshSound() {
+        boolean silent = getSilentMode();
+        mSoundButton.setImageResource(!silent 
+                                         ? R.drawable.ic_sysbar_sound_on
+                                         : R.drawable.ic_sysbar_sound_off);
+        mSoundButton.setBackgroundResource(!silent 
+                                         ? R.drawable.sysbar_toggle_bg_on
+                                         : R.drawable.sysbar_toggle_bg_off);
+    }
+
+    void toggleBluetooth() {
+        if (mBluetoothAdapter == null) return;
+        if (mBluetoothAdapter.isEnabled()) {
+            mBluetoothAdapter.disable();
+        } else {
+            mBluetoothAdapter.enable();
+        }
+    }
+
+    void refreshBluetooth() {
+        boolean on = mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
+        mBluetoothButton.setImageResource(on ? R.drawable.ic_sysbar_bluetooth_on
+                                             : R.drawable.ic_sysbar_bluetooth_off);
+        mBluetoothButton.setBackgroundResource(on
+                                         ? R.drawable.sysbar_toggle_bg_on
+                                         : R.drawable.sysbar_toggle_bg_off);
+    }
+
+    private boolean isGpsEnabled() {
+        ContentResolver res = mContext.getContentResolver();
+        return Settings.Secure.isLocationProviderEnabled(
+                                res, LocationManager.GPS_PROVIDER);
+    }
+
+    private void toggleGps() {
+        Settings.Secure.setLocationProviderEnabled(mContext.getContentResolver(),
+                LocationManager.GPS_PROVIDER, !isGpsEnabled());
+    }
+
+    private void refreshGps() {
+        boolean on = isGpsEnabled();
+        mGpsButton.setImageResource(on ? R.drawable.ic_sysbar_gps_on
+                                       : R.drawable.ic_sysbar_gps_off);
+        mGpsButton.setBackgroundResource(on
+                                         ? R.drawable.sysbar_toggle_bg_on
+                                         : R.drawable.sysbar_toggle_bg_off);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index 312c5f44..6f74924 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -16,13 +16,9 @@
 
 package com.android.systemui.statusbar.tablet;
 
-import android.animation.Animator;
 import android.app.ActivityManagerNative;
-import android.app.Notification;
 import android.app.PendingIntent;
-import android.app.Service;
 import android.app.StatusBarManager;
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -33,13 +29,13 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.view.animation.AnimationUtils;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
-import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
@@ -48,16 +44,18 @@
 import android.widget.TextView;
 
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarIconList;
 import com.android.internal.statusbar.StatusBarNotification;
 
 import com.android.systemui.statusbar.*;
+import com.android.systemui.recent.RecentApplicationsActivity;
 import com.android.systemui.R;
 
 public class TabletStatusBarService extends StatusBarService {
     public static final boolean DEBUG = false;
     public static final String TAG = "TabletStatusBar";
 
+    private static final int MAX_IMAGE_LEVEL = 10000;
+
 
 
     int mIconSize;
@@ -78,6 +76,10 @@
 
     ImageView mBatteryMeter;
     ImageView mSignalMeter;
+    ImageView mSignalIcon;
+
+    View mBarContents;
+    View mCurtains;
 
     NotificationIconArea.IconLayout mIconLayout;
 
@@ -90,16 +92,13 @@
     int mDisabled = 0;
 
     protected void addPanelWindows() {
-        mNotificationPanel = View.inflate(this, R.layout.sysbar_panel_notifications, null);
-        mSystemPanel = (SystemPanel) View.inflate(this, R.layout.sysbar_panel_system, null);
-
-        mNotificationPanel.setVisibility(View.GONE);
-        mSystemPanel.setVisibility(View.GONE);
-
         final Resources res = getResources();
         final int barHeight= res.getDimensionPixelSize(
             com.android.internal.R.dimen.status_bar_height);
 
+        mNotificationPanel = View.inflate(this, R.layout.sysbar_panel_notifications, null);
+        mNotificationPanel.setVisibility(View.GONE);
+
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 400, // ViewGroup.LayoutParams.WRAP_CONTENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT,
@@ -116,8 +115,11 @@
 
         WindowManagerImpl.getDefault().addView(mNotificationPanel, lp);
 
+        mSystemPanel = (SystemPanel) View.inflate(this, R.layout.sysbar_panel_system, null);
+        mSystemPanel.setVisibility(View.GONE);
+
         lp = new WindowManager.LayoutParams(
-                500, // ViewGroup.LayoutParams.WRAP_CONTENT,
+                800,
                 ViewGroup.LayoutParams.WRAP_CONTENT,
                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
@@ -147,6 +149,19 @@
         final View sb = View.inflate(this, R.layout.status_bar, null);
         mStatusBarView = sb;
 
+        mBarContents = sb.findViewById(R.id.bar_contents);
+        mCurtains = sb.findViewById(R.id.lights_out);
+        View systemInfo = sb.findViewById(R.id.systemInfo);
+        View.OnLongClickListener toggle = new View.OnLongClickListener() {
+            public boolean onLongClick(View v) {
+                toggleLightsOut(v);
+                return true;
+            }
+        };
+        
+        systemInfo.setOnLongClickListener(toggle);
+        mCurtains.setOnLongClickListener(toggle);
+
         // the more notifications icon
         mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
 
@@ -158,6 +173,7 @@
         // System info (center)
         mBatteryMeter = (ImageView) sb.findViewById(R.id.battery);
         mSignalMeter = (ImageView) sb.findViewById(R.id.signal);
+        mSignalIcon = (ImageView) sb.findViewById(R.id.signal_icon);
 
         // Add the windows
         addPanelWindows();
@@ -209,18 +225,23 @@
     
     public void setBatteryMeter(int level, boolean plugged) {
         if (DEBUG) Slog.d(TAG, "battery=" + level + (plugged ? " - plugged" : " - unplugged"));
-        mBatteryMeter.setImageResource(plugged ? R.drawable.battery_charging : R.drawable.battery);
-        mBatteryMeter.setImageLevel(level);
+        mBatteryMeter.setImageResource(R.drawable.sysbar_batterymini);
+        // adjust percent to permyriad for ClipDrawable's sake
+        mBatteryMeter.setImageLevel(level * (MAX_IMAGE_LEVEL / 100));
     }
 
     public void setSignalMeter(int level, boolean isWifi) {
         if (DEBUG) Slog.d(TAG, "signal=" + level);
         if (level < 0) {
-            mSignalMeter.setImageResource(isWifi ? R.drawable.wifi_scan : R.drawable.signal_scan);
+            mSignalMeter.setImageDrawable(null);
             mSignalMeter.setImageLevel(0);
+            mSignalIcon.setImageDrawable(null);
         } else {
-            mSignalMeter.setImageResource(isWifi ? R.drawable.wifi : R.drawable.signal);
-            mSignalMeter.setImageLevel(level);
+            mSignalMeter.setImageResource(R.drawable.sysbar_wifimini);
+            // adjust to permyriad
+            mSignalMeter.setImageLevel(level * (MAX_IMAGE_LEVEL / 100));
+            mSignalIcon.setImageResource(isWifi ? R.drawable.ic_sysbar_wifi_mini 
+                                                : R.drawable.ic_sysbar_wifi_mini); // XXX
         }
     }
 
@@ -710,4 +731,28 @@
 
         return true;
     }
+
+    protected void setLightsOut(boolean out) {
+        if (out) {
+            mCurtains.setAnimation(AnimationUtils.loadAnimation((Context)this,
+                        R.anim.lights_out_in));
+            mCurtains.setVisibility(View.VISIBLE);
+            mBarContents.setAnimation(AnimationUtils.loadAnimation((Context)this,
+                        R.anim.status_bar_out));
+            mBarContents.setVisibility(View.GONE);
+        } else {
+            mCurtains.setAnimation(AnimationUtils.loadAnimation((Context)this,
+                        R.anim.lights_out_out));
+            mCurtains.setVisibility(View.GONE);
+            mBarContents.setAnimation(AnimationUtils.loadAnimation((Context)this,
+                        R.anim.status_bar_in));
+            mBarContents.setVisibility(View.VISIBLE);
+        }
+    }
+
+    public void toggleLightsOut(View v) {
+        setLightsOut(mCurtains.getVisibility() != View.VISIBLE);
+    }
 }
+
+
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 88203c3..d19f318 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.policy.impl;
 
+import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+
 import com.android.internal.telephony.IccCard;
 import com.android.internal.widget.LockPatternUtils;
 
@@ -41,6 +43,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.telephony.TelephonyManager;
 import android.util.Config;
 import android.util.EventLog;
@@ -93,6 +96,7 @@
  */
 public class KeyguardViewMediator implements KeyguardViewCallback,
         KeyguardUpdateMonitor.SimStateCallback {
+    private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
     private final static boolean DEBUG = false && Config.LOGD;
     private final static boolean DBG_WAKE = DEBUG || true;
 
@@ -133,7 +137,7 @@
      * turning on the keyguard (i.e, the user has this much time to turn
      * the screen back on without having to face the keyguard).
      */
-    private static final int KEYGUARD_DELAY_MS = 5000;
+    private static final int KEYGUARD_LOCK_AFTER_DELAY_DEFAULT = 5000;
 
     /**
      * How long we'll wait for the {@link KeyguardViewCallback#keyguardDoneDrawing()}
@@ -244,6 +248,7 @@
      * the keyguard.
      */
     private boolean mWaitingUntilKeyguardVisible = false;
+    private LockPatternUtils mLockPatternUtils;
 
     public KeyguardViewMediator(Context context, PhoneWindowManager callback,
             LocalPowerManager powerManager) {
@@ -275,8 +280,9 @@
 
         mUpdateMonitor.registerSimStateCallback(this);
 
-        mKeyguardViewProperties = new LockPatternKeyguardViewProperties(
-                new LockPatternUtils(mContext), mUpdateMonitor);
+        mLockPatternUtils = new LockPatternUtils(mContext);
+        mKeyguardViewProperties 
+                = new LockPatternKeyguardViewProperties(mLockPatternUtils, mUpdateMonitor);
 
         mKeyguardViewManager = new KeyguardViewManager(
                 context, WindowManagerImpl.getDefault(), this,
@@ -326,15 +332,46 @@
                 // to enable it a little bit later (i.e, give the user a chance
                 // to turn the screen back on within a certain window without
                 // having to unlock the screen)
-                long when = SystemClock.elapsedRealtime() + KEYGUARD_DELAY_MS;
-                Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
-                intent.putExtra("seq", mDelayedShowingSequence);
-                PendingIntent sender = PendingIntent.getBroadcast(mContext,
-                        0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
-                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when,
-                        sender);
-                if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
-                                 + mDelayedShowingSequence);
+                final ContentResolver cr = mContext.getContentResolver();
+
+                // From DisplaySettings
+                long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT,
+                        KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT);
+
+                // From SecuritySettings
+                final long lockAfterTimeout = Settings.Secure.getInt(cr,
+                        Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+                        KEYGUARD_LOCK_AFTER_DELAY_DEFAULT);
+
+                // From DevicePolicyAdmin
+                final long policyTimeout = mLockPatternUtils.getDevicePolicyManager()
+                        .getMaximumTimeToLock(null);
+
+                long timeout;
+                if (policyTimeout > 0) {
+                    // policy in effect. Make sure we don't go beyond policy limit.
+                    displayTimeout = Math.max(displayTimeout, 0); // ignore negative values
+                    timeout = Math.min(policyTimeout - displayTimeout, lockAfterTimeout);
+                } else {
+                    timeout = lockAfterTimeout;
+                }
+
+                if (timeout <= 0) {
+                    // Lock now
+                    mSuppressNextLockSound = true;
+                    doKeyguard();
+                } else {
+                    // Lock in the future
+                    long when = SystemClock.elapsedRealtime() + timeout;
+                    Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
+                    intent.putExtra("seq", mDelayedShowingSequence);
+                    PendingIntent sender = PendingIntent.getBroadcast(mContext,
+                            0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when,
+                            sender);
+                    if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
+                                     + mDelayedShowingSequence);
+                }
             } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
                 // Do not enable the keyguard if the prox sensor forced the screen off.
             } else {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 1acdaaf..3770b55 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -5992,12 +5992,14 @@
                                             p,
                                             &rsize,
                                             &reply);
-            if (ret == NO_ERROR) {
-                if (reply != NO_ERROR) {
-                    status = reply;
-                }
-            } else {
+            // stop at first error encountered
+            if (ret != NO_ERROR) {
                 status = ret;
+                *(int *)pReplyData = reply;
+                break;
+            } else if (reply != NO_ERROR) {
+                *(int *)pReplyData = reply;
+                break;
             }
             mCblk->serverIndex += size;
         }
@@ -6005,8 +6007,10 @@
         mCblk->clientIndex = 0;
         return status;
     } else if (cmdCode == EFFECT_CMD_ENABLE) {
+        *(int *)pReplyData = NO_ERROR;
         return enable();
     } else if (cmdCode == EFFECT_CMD_DISABLE) {
+        *(int *)pReplyData = NO_ERROR;
         return disable();
     }
 
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 54ff69f..b6d725f 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -33,6 +33,7 @@
 import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -105,7 +106,7 @@
     private int mNumDnsEntries;
 
     private boolean mTestMode;
-    private static volatile ConnectivityService sServiceInstance;
+    private static ConnectivityService sServiceInstance;
 
     private Handler mHandler;
 
@@ -158,51 +159,20 @@
     }
     RadioAttributes[] mRadioAttributes;
 
-    private static class ConnectivityThread extends Thread {
-        private Context mContext;
-
-        private ConnectivityThread(Context context) {
-            super("ConnectivityThread");
-            mContext = context;
+    public static synchronized ConnectivityService getInstance(Context context) {
+        if (sServiceInstance == null) {
+            sServiceInstance = new ConnectivityService(context);
         }
-
-        @Override
-        public void run() {
-            Looper.prepare();
-            synchronized (this) {
-                sServiceInstance = new ConnectivityService(mContext);
-                notifyAll();
-            }
-            Looper.loop();
-        }
-
-        public static synchronized ConnectivityService getServiceInstance(Context context) {
-            if (sServiceInstance == null) {
-                ConnectivityThread thread = new ConnectivityThread(context);
-                thread.start();
-                while (sServiceInstance == null) {
-                    try {
-                        // Wait until sServiceInstance has been initialized.
-                        thread.wait();
-                    } catch (InterruptedException ignore) {
-                        Slog.e(TAG,
-                            "Unexpected InterruptedException while waiting"+
-                            " for ConnectivityService thread");
-                    }
-                }
-            }
-
-            return sServiceInstance;
-        }
-    }
-
-    public static ConnectivityService getInstance(Context context) {
-        return ConnectivityThread.getServiceInstance(context);
+        return sServiceInstance;
     }
 
     private ConnectivityService(Context context) {
         if (DBG) Slog.v(TAG, "ConnectivityService starting up");
 
+        HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
+        handlerThread.start();
+        mHandler = new MyHandler(handlerThread.getLooper());
+
         // setup our unique device name
         String id = Settings.Secure.getString(context.getContentResolver(),
                 Settings.Secure.ANDROID_ID);
@@ -233,7 +203,6 @@
 
         mNetTrackers = new NetworkStateTracker[
                 ConnectivityManager.MAX_NETWORK_TYPE+1];
-        mHandler = new MyHandler();
 
         mNetworkPreference = getPersistedNetworkPreference();
 
@@ -1579,6 +1548,10 @@
 
     // must be stateless - things change under us.
     private class MyHandler extends Handler {
+        public MyHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             NetworkInfo info;
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 4a0df59..0b1a4a3 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -69,10 +69,12 @@
     private static final int DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes
     private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
     private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
+    private static final int DEFAULT_FULL_THRESHOLD_BYTES = 1024*1024; // 1MB
     private long mFreeMem;  // on /data
     private long mLastReportedFreeMem;
     private long mLastReportedFreeMemTime;
     private boolean mLowMemFlag=false;
+    private boolean mMemFullFlag=false;
     private Context mContext;
     private ContentResolver mContentResolver;
     private long mTotalMemory;  // on /data
@@ -87,9 +89,13 @@
     private boolean mClearingCache;
     private Intent mStorageLowIntent;
     private Intent mStorageOkIntent;
+    private Intent mStorageFullIntent;
+    private Intent mStorageNotFullIntent;
     private CachePackageDataObserver mClearCacheObserver;
     private static final int _TRUE = 1;
     private static final int _FALSE = 0;
+    private long mMemLowThreshold;
+    private int mMemFullThreshold;
 
     /**
      * This string is used for ServiceManager access to this class.
@@ -103,7 +109,7 @@
     Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
-            //dont handle an invalid message
+            //don't handle an invalid message
             if (msg.what != DEVICE_MEMORY_WHAT) {
                 Slog.e(TAG, "Will not process invalid message");
                 return;
@@ -184,7 +190,7 @@
         try {
             if (localLOGV) Slog.i(TAG, "Clearing cache");
             IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
-                    freeStorageAndNotify(getMemThreshold(), mClearCacheObserver);
+                    freeStorageAndNotify(mMemLowThreshold, mClearCacheObserver);
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
             mClearingCache = false;
@@ -209,8 +215,7 @@
             if (localLOGV)  Slog.v(TAG, "freeMemory="+mFreeMem);
 
             //post intent to NotificationManager to display icon if necessary
-            long memThreshold = getMemThreshold();
-            if (mFreeMem < memThreshold) {
+            if (mFreeMem < mMemLowThreshold) {
                 if (!mLowMemFlag) {
                     if (checkCache) {
                         // See if clearing cache helps
@@ -235,6 +240,17 @@
                     mLowMemFlag = false;
                 }
             }
+            if (mFreeMem < mMemFullThreshold) {
+                if (!mMemFullFlag) {
+                    sendFullNotification();
+                    mMemFullFlag = true;
+                }
+            } else {
+                if (mMemFullFlag) {
+                    cancelFullNotification();
+                    mMemFullFlag = false;
+                }
+            }
         }
         if(localLOGV) Slog.i(TAG, "Posting Message again");
         //keep posting messages to itself periodically
@@ -264,6 +280,20 @@
         return mTotalMemory*value;
     }
 
+    /*
+     * just query settings to retrieve the memory full threshold.
+     * Preferred this over using a ContentObserver since Settings.Secure caches the value
+     * any way
+     */
+    private int getMemFullThreshold() {
+        int value = Settings.Secure.getInt(
+                              mContentResolver,
+                              Settings.Secure.SYS_STORAGE_FULL_THRESHOLD_BYTES,
+                              DEFAULT_FULL_THRESHOLD_BYTES);
+        if(localLOGV) Slog.v(TAG, "Full Threshold Bytes="+value);
+        return value;
+    }
+
     /**
     * Constructor to run service. initializes the disk space threshold value
     * and posts an empty message to kickstart the process.
@@ -283,6 +313,13 @@
         mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
         mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
         mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
+        mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
+        mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        // cache storage thresholds
+        mMemLowThreshold = getMemThreshold();
+        mMemFullThreshold = getMemFullThreshold();
         checkMemory(true);
     }
 
@@ -332,6 +369,23 @@
         mContext.sendBroadcast(mStorageOkIntent);
     }
 
+    /**
+     * Send a notification when storage is full.
+     */
+    private final void sendFullNotification() {
+        if(localLOGV) Slog.i(TAG, "Sending memory full notification");
+        mContext.sendStickyBroadcast(mStorageFullIntent);
+    }
+
+    /**
+     * Cancels memory full notification and sends "not full" intent.
+     */
+    private final void cancelFullNotification() {
+        if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
+        mContext.removeStickyBroadcast(mStorageFullIntent);
+        mContext.sendBroadcast(mStorageNotFullIntent);
+    }
+
     public void updateMemory() {
         int callingUid = getCallingUid();
         if(callingUid != Process.SYSTEM_UID) {
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
index 8da0cf1..dbc59ef 100644
--- a/services/java/com/android/server/InputWindow.java
+++ b/services/java/com/android/server/InputWindow.java
@@ -34,9 +34,17 @@
     // Dispatching timeout.
     public long dispatchingTimeoutNanos;
     
-    // Window frame position.
+    // Window frame area.
     public int frameLeft;
     public int frameTop;
+    public int frameRight;
+    public int frameBottom;
+    
+    // Window visible frame area.
+    public int visibleFrameLeft;
+    public int visibleFrameTop;
+    public int visibleFrameRight;
+    public int visibleFrameBottom;
     
     // Window touchable area.
     public int touchableAreaLeft;
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d604886..c07007a 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -68,6 +68,8 @@
 
     private static final String TAG = "MountService";
 
+    private static final String VOLD_TAG = "VoldConnector";
+
     /*
      * Internal vold volume state constants
      */
@@ -1006,9 +1008,15 @@
             return;
         }
 
-        mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
+        /*
+         * Create the connection to vold with a maximum queue of twice the
+         * amount of containers we'd ever expect to have. This keeps an
+         * "asec list" from blocking a thread repeatedly.
+         */
+        mConnector = new NativeDaemonConnector(this, "vold",
+                PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);
         mReady = false;
-        Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
+        Thread thread = new Thread(mConnector, VOLD_TAG);
         thread.start();
     }
 
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 249c32b..03f8dae 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -109,6 +109,10 @@
                 int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
                 if (count < 0) break;
 
+                // Add our starting point to the count and reset the start.
+                count += start;
+                start = 0;
+
                 for (int i = 0; i < count; i++) {
                     if (buffer[i] == 0) {
                         String event = new String(buffer, start, i - start);
@@ -141,6 +145,9 @@
                         start = i + 1;
                     }
                 }
+
+                // We should end at the amount we read. If not, compact then
+                // buffer and read again.
                 if (start != count) {
                     final int remaining = BUFFER_SIZE - start;
                     System.arraycopy(buffer, start, buffer, 0, remaining);
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index cb8fa35..de459ab 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -55,6 +55,8 @@
 
     private static final String TAG = "NetworkManagmentService";
 
+    private static final String NETD_TAG = "NetdConnector";
+
     class NetdResponseCode {
         public static final int InterfaceListResult       = 110;
         public static final int TetherInterfaceListResult = 111;
@@ -101,8 +103,8 @@
         }
 
         mConnector = new NativeDaemonConnector(
-                new NetdCallbackReceiver(), "netd", 10, "NetdConnector");
-        Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
+                new NetdCallbackReceiver(), "netd", 10, NETD_TAG);
+        Thread thread = new Thread(mConnector, NETD_TAG);
         thread.start();
     }
 
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 1bfeb79..5ae87cf 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -2022,7 +2022,11 @@
                     ActivityInfo ai = getActivityInfo(pa.mActivity, flags);
                     if (DEBUG_PREFERRED) {
                         Log.v(TAG, "Got preferred activity:");
-                        ai.dump(new LogPrinter(Log.INFO, TAG), "  ");
+                        if (ai != null) {
+                            ai.dump(new LogPrinter(Log.VERBOSE, TAG), "  ");
+                        } else {
+                            Log.v(TAG, "  null");
+                        }
                     }
                     if (ai != null) {
                         for (int j=0; j<N; j++) {
@@ -2603,7 +2607,7 @@
         }
         // First check if this is a system package that may involve an update
         if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
-            if (!ps.codePath.equals(scanFile)) {
+            if (ps != null && !ps.codePath.equals(scanFile)) {
                 // The path has changed from what was last scanned...  check the
                 // version of the new path against what we have stored to determine
                 // what to do.
@@ -6181,18 +6185,15 @@
         }
         // Delete the updated package
         outInfo.isRemovedPackageSystemUpdate = true;
-        boolean deleteCodeAndResources = false;
-        if (ps.versionCode <  p.mVersionCode) {
+        final boolean deleteCodeAndResources;
+        if (ps.versionCode < p.mVersionCode) {
             // Delete code and resources for downgrades
             deleteCodeAndResources = true;
-            if ((flags & PackageManager.DONT_DELETE_DATA) == 0) {
-                flags &= ~PackageManager.DONT_DELETE_DATA;
-            }
+            flags &= ~PackageManager.DONT_DELETE_DATA;
         } else {
             // Preserve data by setting flag
-            if ((flags & PackageManager.DONT_DELETE_DATA) == 0) {
-                flags |= PackageManager.DONT_DELETE_DATA;
-            }
+            deleteCodeAndResources = false;
+            flags |= PackageManager.DONT_DELETE_DATA;
         }
         boolean ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo);
         if (!ret) {
@@ -9386,17 +9387,18 @@
 
     // ------- apps on sdcard specific code -------
     static final boolean DEBUG_SD_INSTALL = false;
-    final private String mSdEncryptKey = "AppsOnSD";
-    final private String mSdEncryptAlg = "AES";
+    private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD";
+    private static final String SD_ENCRYPTION_ALGORITHM = "AES";
+    static final int MAX_CONTAINERS = 250;
     private boolean mMediaMounted = false;
-    private static final int MAX_CONTAINERS = 250;
 
     private String getEncryptKey() {
         try {
-            String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey);
+            String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
+                    SD_ENCRYPTION_KEYSTORE_NAME);
             if (sdEncKey == null) {
-                sdEncKey = SystemKeyStore.getInstance().
-                        generateNewKeyHexString(128, mSdEncryptAlg, mSdEncryptKey);
+                sdEncKey = SystemKeyStore.getInstance().generateNewKeyHexString(128,
+                        SD_ENCRYPTION_ALGORITHM, SD_ENCRYPTION_KEYSTORE_NAME);
                 if (sdEncKey == null) {
                     Slog.e(TAG, "Failed to create encryption keys");
                     return null;
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 16f3f10..2e32e60 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -2456,7 +2456,8 @@
             }
             mKeylightDelay = LONG_KEYLIGHT_DELAY;
             if (totalDelay < 0) {
-                mScreenOffDelay = Integer.MAX_VALUE;
+                // negative number means stay on as long as possible.
+                mScreenOffDelay = mMaximumScreenOffTimeout;
             } else if (mKeylightDelay < totalDelay) {
                 // subtract the time that the keylight delay. This will give us the
                 // remainder of the time that we need to sleep to get the accurate
diff --git a/services/java/com/android/server/ShutdownActivity.java b/services/java/com/android/server/ShutdownActivity.java
index 64b9c5d..c9d4d01 100644
--- a/services/java/com/android/server/ShutdownActivity.java
+++ b/services/java/com/android/server/ShutdownActivity.java
@@ -27,19 +27,26 @@
 public class ShutdownActivity extends Activity {
 
     private static final String TAG = "ShutdownActivity";
+    private boolean mReboot;
     private boolean mConfirm;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
+        Intent intent = getIntent();
+        mReboot = Intent.ACTION_REBOOT.equals(intent.getAction());
+        mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
         Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
 
         Handler h = new Handler();
         h.post(new Runnable() {
             public void run() {
-                ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
+                if (mReboot) {
+                    ShutdownThread.reboot(ShutdownActivity.this, null, mConfirm);
+                } else {
+                    ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
+                }
             }
         });
     }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d44ce97..dffc31c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -431,8 +431,11 @@
             }
 
             try {
-                Slog.i(TAG, "Sip Service");
-                ServiceManager.addService("sip", new SipService(context));
+                SipService sipService = SipService.create(context);
+                if (sipService != null) {
+                    Slog.i(TAG, "Sip Service");
+                    ServiceManager.addService("sip", sipService);
+                }
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting SIP Service", e);
             }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 79bde7c..2ab8091 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5208,6 +5208,14 @@
                 final Rect frame = child.mFrame;
                 inputWindow.frameLeft = frame.left;
                 inputWindow.frameTop = frame.top;
+                inputWindow.frameRight = frame.right;
+                inputWindow.frameBottom = frame.bottom;
+                
+                final Rect visibleFrame = child.mVisibleFrame;
+                inputWindow.visibleFrameLeft = visibleFrame.left;
+                inputWindow.visibleFrameTop = visibleFrame.top;
+                inputWindow.visibleFrameRight = visibleFrame.right;
+                inputWindow.visibleFrameBottom = visibleFrame.bottom;
                 
                 switch (child.mTouchableInsets) {
                     default:
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 3cc60da..7d8c307 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -132,7 +132,8 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 
-public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor {
+public final class ActivityManagerService extends ActivityManagerNative
+        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
     static final String TAG = "ActivityManager";
     static final boolean DEBUG = false;
     static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
@@ -751,6 +752,7 @@
     boolean mBooting = false;
     boolean mWaitingUpdate = false;
     boolean mDidUpdate = false;
+    boolean mOnBattery = false;
 
     Context mContext;
 
@@ -1382,8 +1384,10 @@
                 systemDir, "batterystats.bin").toString());
         mBatteryStatsService.getActiveStatistics().readLocked();
         mBatteryStatsService.getActiveStatistics().writeLocked();
+        mOnBattery = mBatteryStatsService.getActiveStatistics().getIsOnBattery();
+        mBatteryStatsService.getActiveStatistics().setCallback(this);
         
-        mUsageStatsService = new UsageStatsService( new File(
+        mUsageStatsService = new UsageStatsService(new File(
                 systemDir, "usagestats").toString());
 
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
@@ -1496,25 +1500,36 @@
             synchronized(bstats) {
                 synchronized(mPidsSelfLocked) {
                     if (haveNewCpuStats) {
-                        if (mBatteryStatsService.isOnBattery()) {
+                        if (mOnBattery) {
+                            int perc = bstats.startAddingCpuLocked();
+                            int totalUTime = 0;
+                            int totalSTime = 0;
                             final int N = mProcessStats.countWorkingStats();
                             for (int i=0; i<N; i++) {
                                 ProcessStats.Stats st
                                         = mProcessStats.getWorkingStats(i);
                                 ProcessRecord pr = mPidsSelfLocked.get(st.pid);
+                                int otherUTime = (st.rel_utime*perc)/100;
+                                int otherSTime = (st.rel_stime*perc)/100;
+                                totalUTime += otherUTime;
+                                totalSTime += otherSTime;
                                 if (pr != null) {
                                     BatteryStatsImpl.Uid.Proc ps = pr.batteryStats;
-                                    ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+                                    ps.addCpuTimeLocked(st.rel_utime-otherUTime,
+                                            st.rel_stime-otherSTime);
                                     ps.addSpeedStepTimes(cpuSpeedTimes);
                                 } else {
                                     BatteryStatsImpl.Uid.Proc ps =
                                             bstats.getProcessStatsLocked(st.name, st.pid);
                                     if (ps != null) {
-                                        ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+                                        ps.addCpuTimeLocked(st.rel_utime-otherUTime,
+                                                st.rel_stime-otherSTime);
                                         ps.addSpeedStepTimes(cpuSpeedTimes);
                                     }
                                 }
                             }
+                            bstats.finishAddingCpuLocked(perc, totalUTime,
+                                    totalSTime, cpuSpeedTimes);
                         }
                     }
                 }
@@ -1527,6 +1542,23 @@
         }
     }
     
+    @Override
+    public void batteryNeedsCpuUpdate() {
+        updateCpuStatsNow();
+    }
+
+    @Override
+    public void batteryPowerChanged(boolean onBattery) {
+        // When plugging in, update the CPU stats first before changing
+        // the plug state.
+        updateCpuStatsNow();
+        synchronized (this) {
+            synchronized(mPidsSelfLocked) {
+                mOnBattery = onBattery;
+            }
+        }
+    }
+
     /**
      * Initialize the application bind args. These are passed to each
      * process when the bindApplication() IPC is sent to the process. They're
@@ -6858,7 +6890,7 @@
                                 app.pid, app.getPackageList());
                     currApp.uid = app.info.uid;
                     if (mHeavyWeightProcess == app) {
-                        currApp.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HEAVY_WEIGHT;
+                        currApp.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
                     }
                     int adj = app.curAdj;
                     if (adj >= EMPTY_APP_ADJ) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c21a221..5f79b85 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2885,7 +2885,8 @@
                     + " res=" + resultCode + " data=" + resultData);
             if (r.info.applicationInfo.uid > 0) {
                 mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
-                        r.packageName, resultData, r.getUriPermissionsLocked());
+                        resultTo.packageName, resultData, 
+                        resultTo.getUriPermissionsLocked());
             }
             resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
                                      resultData);
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 6d1fbab..67df707 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -131,6 +131,13 @@
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
 
+        long wtime;
+        synchronized (batteryStats.getBatteryStats()) {
+            wtime = batteryStats.getBatteryStats().getProcessWakeTime(info.uid,
+                    pid, SystemClock.elapsedRealtime());
+        }
+        long timeUsed = wtime - lastWakeTime;
+
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
         }
@@ -182,7 +189,9 @@
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.println(lruSeq);
         pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
-                pw.print(" lastRequestedGc=");
+                pw.print(" time used=");
+                TimeUtils.formatDuration(timeUsed, pw); pw.println("");
+        pw.print(prefix); pw.print("lastRequestedGc=");
                 TimeUtils.formatDuration(lastRequestedGc, now, pw);
                 pw.print(" lastLowMemory=");
                 TimeUtils.formatDuration(lastLowMemory, now, pw);
diff --git a/services/java/com/android/server/sip/SipHelper.java b/services/java/com/android/server/sip/SipHelper.java
index 83eeb84..d9a1bbf 100644
--- a/services/java/com/android/server/sip/SipHelper.java
+++ b/services/java/com/android/server/sip/SipHelper.java
@@ -20,7 +20,6 @@
 import gov.nist.javax.sip.clientauthutils.AccountManager;
 import gov.nist.javax.sip.clientauthutils.AuthenticationHelper;
 
-import android.net.sip.SessionDescription;
 import android.net.sip.SipProfile;
 import android.util.Log;
 
@@ -243,7 +242,7 @@
     }
 
     public ClientTransaction sendInvite(SipProfile caller, SipProfile callee,
-            SessionDescription sessionDescription, String tag)
+            String sessionDescription, String tag)
             throws SipException {
         try {
             FromHeader fromHeader = createFromHeader(caller, tag);
@@ -259,9 +258,9 @@
                     toHeader, viaHeaders, maxForwards);
 
             request.addHeader(createContactHeader(caller));
-            request.setContent(sessionDescription.getContent(),
+            request.setContent(sessionDescription,
                     mHeaderFactory.createContentTypeHeader(
-                            "application", sessionDescription.getType()));
+                            "application", "sdp"));
 
             ClientTransaction clientTransaction =
                     mSipProvider.getNewClientTransaction(request);
@@ -273,12 +272,12 @@
     }
 
     public ClientTransaction sendReinvite(Dialog dialog,
-            SessionDescription sessionDescription) throws SipException {
+            String sessionDescription) throws SipException {
         try {
             Request request = dialog.createRequest(Request.INVITE);
-            request.setContent(sessionDescription.getContent(),
+            request.setContent(sessionDescription,
                     mHeaderFactory.createContentTypeHeader(
-                            "application", sessionDescription.getType()));
+                            "application", "sdp"));
 
             ClientTransaction clientTransaction =
                     mSipProvider.getNewClientTransaction(request);
@@ -326,7 +325,7 @@
      * @param event the INVITE request event
      */
     public ServerTransaction sendInviteOk(RequestEvent event,
-            SipProfile localProfile, SessionDescription sessionDescription,
+            SipProfile localProfile, String sessionDescription,
             ServerTransaction inviteTransaction)
             throws SipException {
         try {
@@ -334,9 +333,9 @@
             Response response = mMessageFactory.createResponse(Response.OK,
                     request);
             response.addHeader(createContactHeader(localProfile));
-            response.setContent(sessionDescription.getContent(),
+            response.setContent(sessionDescription,
                     mHeaderFactory.createContentTypeHeader(
-                            "application", sessionDescription.getType()));
+                            "application", "sdp"));
 
             if (inviteTransaction == null) {
                 inviteTransaction = getServerTransaction(event);
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
index 3dcaff6..563ce58 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/services/java/com/android/server/sip/SipService.java
@@ -54,6 +54,7 @@
 import javax.sip.SipException;
 
 /**
+ * @hide
  */
 public final class SipService extends ISipService.Stub {
     private static final String TAG = "SipService";
@@ -78,7 +79,16 @@
 
     private ConnectivityReceiver mConnectivityReceiver;
 
-    public SipService(Context context) {
+    /**
+     * Creates a {@code SipService} instance. Returns null if SIP API is not
+     * supported.
+     */
+    public static SipService create(Context context) {
+        return (SipManager.isApiSupported(context) ? new SipService(context)
+                                                   : null);
+    }
+
+    private SipService(Context context) {
         Log.v(TAG, " service started!");
         mContext = context;
         mConnectivityReceiver = new ConnectivityReceiver();
@@ -435,7 +445,7 @@
 
         @Override
         public void onRinging(ISipSession session, SipProfile caller,
-                byte[] sessionDescription) {
+                String sessionDescription) {
             synchronized (SipService.this) {
                 try {
                     if (!isRegistered()) {
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
index d33558b..70ff9ea 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -20,6 +20,7 @@
 import gov.nist.javax.sip.clientauthutils.UserCredentials;
 import gov.nist.javax.sip.header.SIPHeaderNames;
 import gov.nist.javax.sip.header.WWWAuthenticate;
+import gov.nist.javax.sip.message.SIPMessage;
 
 import android.net.sip.ISipSession;
 import android.net.sip.ISipSessionListener;
@@ -31,6 +32,7 @@
 import android.util.Log;
 
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.net.DatagramSocket;
 import java.text.ParseException;
 import java.util.Collection;
@@ -121,6 +123,7 @@
         properties.setProperty("javax.sip.STACK_NAME", getStackName());
         String outboundProxy = myself.getProxyAddress();
         if (!TextUtils.isEmpty(outboundProxy)) {
+            Log.v(TAG, "outboundProxy is " + outboundProxy);
             properties.setProperty("javax.sip.OUTBOUND_PROXY", outboundProxy
                     + ":" + myself.getPort() + "/" + myself.getProtocol());
         }
@@ -284,6 +287,22 @@
         }
     }
 
+    private String extractContent(Message message) {
+        // Currently we do not support secure MIME bodies.
+        byte[] bytes = message.getRawContent();
+        if (bytes != null) {
+            try {
+                if (message instanceof SIPMessage) {
+                    return ((SIPMessage) message).getMessageContent();
+                } else {
+                    return new String(bytes, "UTF-8");
+                }
+            } catch (UnsupportedEncodingException e) {
+            }
+        }
+        return null;
+    }
+
     private class SipSessionCallReceiverImpl extends SipSessionImpl {
         public SipSessionCallReceiverImpl(ISipSessionListener listener) {
             super(listener);
@@ -302,7 +321,7 @@
                 newSession.mPeerProfile = createPeerProfile(event.getRequest());
                 newSession.mState = SipSessionState.INCOMING_CALL;
                 newSession.mPeerSessionDescription =
-                        event.getRequest().getRawContent();
+                        extractContent(event.getRequest());
                 addSipSession(newSession);
                 mProxy.onRinging(newSession, newSession.mPeerProfile,
                         newSession.mPeerSessionDescription);
@@ -321,7 +340,7 @@
         Dialog mDialog;
         ServerTransaction mServerTransaction;
         ClientTransaction mClientTransaction;
-        byte[] mPeerSessionDescription;
+        String mPeerSessionDescription;
         boolean mInCall;
         boolean mReRegisterFlag = false;
 
@@ -401,12 +420,12 @@
         }
 
         public void makeCall(SipProfile peerProfile,
-                SessionDescription sessionDescription) {
+                String sessionDescription) {
             doCommandAsync(
                     new MakeCallCommand(peerProfile, sessionDescription));
         }
 
-        public void answerCall(SessionDescription sessionDescription) {
+        public void answerCall(String sessionDescription) {
             try {
                 processCommand(
                         new MakeCallCommand(mPeerProfile, sessionDescription));
@@ -419,7 +438,7 @@
             doCommandAsync(END_CALL);
         }
 
-        public void changeCall(SessionDescription sessionDescription) {
+        public void changeCall(String sessionDescription) {
             doCommandAsync(
                     new MakeCallCommand(mPeerProfile, sessionDescription));
         }
@@ -726,10 +745,9 @@
             if (evt instanceof MakeCallCommand) {
                 MakeCallCommand cmd = (MakeCallCommand) evt;
                 mPeerProfile = cmd.getPeerProfile();
-                SessionDescription sessionDescription =
-                        cmd.getSessionDescription();
                 mClientTransaction = mSipHelper.sendInvite(mLocalProfile,
-                        mPeerProfile, sessionDescription, generateTag());
+                        mPeerProfile, cmd.getSessionDescription(),
+                        generateTag());
                 mDialog = mClientTransaction.getDialog();
                 addSipSession(this);
                 mState = SipSessionState.OUTGOING_CALL;
@@ -811,7 +829,7 @@
                     return true;
                 case Response.OK:
                     mSipHelper.sendInviteAck(event, mDialog);
-                    mPeerSessionDescription = response.getRawContent();
+                    mPeerSessionDescription = extractContent(response);
                     establishCall();
                     return true;
                 case Response.PROXY_AUTHENTICATION_REQUIRED:
@@ -897,7 +915,7 @@
                 // got Re-INVITE
                 RequestEvent event = mInviteReceived = (RequestEvent) evt;
                 mState = SipSessionState.INCOMING_CALL;
-                mPeerSessionDescription = event.getRequest().getRawContent();
+                mPeerSessionDescription = extractContent(event.getRequest());
                 mServerTransaction = null;
                 mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
                 return true;
@@ -1060,10 +1078,10 @@
     }
 
     private class MakeCallCommand extends EventObject {
-        private SessionDescription mSessionDescription;
+        private String mSessionDescription;
 
         public MakeCallCommand(SipProfile peerProfile,
-                SessionDescription sessionDescription) {
+                String sessionDescription) {
             super(peerProfile);
             mSessionDescription = sessionDescription;
         }
@@ -1072,7 +1090,7 @@
             return (SipProfile) getSource();
         }
 
-        public SessionDescription getSessionDescription() {
+        public String getSessionDescription() {
             return mSessionDescription;
         }
     }
diff --git a/services/java/com/android/server/sip/SipSessionListenerProxy.java b/services/java/com/android/server/sip/SipSessionListenerProxy.java
index fd49fd8..7234196 100644
--- a/services/java/com/android/server/sip/SipSessionListenerProxy.java
+++ b/services/java/com/android/server/sip/SipSessionListenerProxy.java
@@ -19,6 +19,7 @@
 import android.net.sip.ISipSession;
 import android.net.sip.ISipSessionListener;
 import android.net.sip.SipProfile;
+import android.os.DeadObjectException;
 import android.util.Log;
 
 /** Class to help safely run a callback in a different thread. */
@@ -49,21 +50,21 @@
                 try {
                     mListener.onCalling(session);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onCalling()", t);
+                    handle(t, "onCalling()");
                 }
             }
         });
     }
 
     public void onRinging(final ISipSession session, final SipProfile caller,
-            final byte[] sessionDescription) {
+            final String sessionDescription) {
         if (mListener == null) return;
         proxy(new Runnable() {
             public void run() {
                 try {
                     mListener.onRinging(session, caller, sessionDescription);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onRinging()", t);
+                    handle(t, "onRinging()");
                 }
             }
         });
@@ -76,21 +77,21 @@
                 try {
                     mListener.onRingingBack(session);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onRingingBack()", t);
+                    handle(t, "onRingingBack()");
                 }
             }
         });
     }
 
     public void onCallEstablished(final ISipSession session,
-            final byte[] sessionDescription) {
+            final String sessionDescription) {
         if (mListener == null) return;
         proxy(new Runnable() {
             public void run() {
                 try {
                     mListener.onCallEstablished(session, sessionDescription);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onCallEstablished()", t);
+                    handle(t, "onCallEstablished()");
                 }
             }
         });
@@ -103,7 +104,7 @@
                 try {
                     mListener.onCallEnded(session);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onCallEnded()", t);
+                    handle(t, "onCallEnded()");
                 }
             }
         });
@@ -116,7 +117,7 @@
                 try {
                     mListener.onCallBusy(session);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onCallBusy()", t);
+                    handle(t, "onCallBusy()");
                 }
             }
         });
@@ -130,7 +131,7 @@
                 try {
                     mListener.onCallChangeFailed(session, className, message);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onCallChangeFailed()", t);
+                    handle(t, "onCallChangeFailed()");
                 }
             }
         });
@@ -144,7 +145,7 @@
                 try {
                     mListener.onError(session, className, message);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onError()", t);
+                    handle(t, "onError()");
                 }
             }
         });
@@ -157,7 +158,7 @@
                 try {
                     mListener.onRegistering(session);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onRegistering()", t);
+                    handle(t, "onRegistering()");
                 }
             }
         });
@@ -171,7 +172,7 @@
                 try {
                     mListener.onRegistrationDone(session, duration);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onRegistrationDone()", t);
+                    handle(t, "onRegistrationDone()");
                 }
             }
         });
@@ -185,7 +186,7 @@
                 try {
                     mListener.onRegistrationFailed(session, className, message);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onRegistrationFailed()", t);
+                    handle(t, "onRegistrationFailed()");
                 }
             }
         });
@@ -198,9 +199,19 @@
                 try {
                     mListener.onRegistrationTimeout(session);
                 } catch (Throwable t) {
-                    Log.w(TAG, "onRegistrationTimeout()", t);
+                    handle(t, "onRegistrationTimeout()");
                 }
             }
         });
     }
+
+    private void handle(Throwable t, String message) {
+        if (t instanceof DeadObjectException) {
+            mListener = null;
+            // This creates race but it's harmless. Just don't log the error
+            // when it happens.
+        } else if (mListener != null) {
+            Log.w(TAG, message, t);
+        }
+    }
 }
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index a237ee9..7af5e95 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -169,6 +169,12 @@
     jfieldID dispatchingTimeoutNanos;
     jfieldID frameLeft;
     jfieldID frameTop;
+    jfieldID frameRight;
+    jfieldID frameBottom;
+    jfieldID visibleFrameLeft;
+    jfieldID visibleFrameTop;
+    jfieldID visibleFrameRight;
+    jfieldID visibleFrameBottom;
     jfieldID touchableAreaLeft;
     jfieldID touchableAreaTop;
     jfieldID touchableAreaRight;
@@ -269,6 +275,7 @@
             nsecs_t& outNewTimeout);
     virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
     virtual nsecs_t getKeyRepeatTimeout();
+    virtual nsecs_t getKeyRepeatDelay();
     virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
     virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
@@ -283,6 +290,12 @@
         nsecs_t dispatchingTimeout;
         int32_t frameLeft;
         int32_t frameTop;
+        int32_t frameRight;
+        int32_t frameBottom;
+        int32_t visibleFrameLeft;
+        int32_t visibleFrameTop;
+        int32_t visibleFrameRight;
+        int32_t visibleFrameBottom;
         int32_t touchableAreaLeft;
         int32_t touchableAreaTop;
         int32_t touchableAreaRight;
@@ -294,10 +307,8 @@
         int32_t ownerPid;
         int32_t ownerUid;
 
-        inline bool touchableAreaContainsPoint(int32_t x, int32_t y) {
-            return x >= touchableAreaLeft && x <= touchableAreaRight
-                    && y >= touchableAreaTop && y <= touchableAreaBottom;
-        }
+        bool visibleFrameIntersects(const InputWindow* other) const;
+        bool touchableAreaContainsPoint(int32_t x, int32_t y) const;
     };
 
     struct InputApplication {
@@ -370,9 +381,13 @@
     // Focus tracking for touch.
     bool mTouchDown;
     InputWindow* mTouchedWindow;                   // primary target for current down
+    bool mTouchedWindowIsObscured;                 // true if other windows may obscure the target
     Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets
-
-    Vector<InputWindow*> mTempTouchedOutsideWindows; // temporary outside touch targets
+    struct OutsideTarget {
+        InputWindow* window;
+        bool obscured;
+    };
+    Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets
     Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
 
     // Focused application.
@@ -391,6 +406,7 @@
     int32_t waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid,
             Vector<InputTarget>& outTargets, InputWindow*& outTouchedWindow);
+    bool isWindowObscuredLocked(const InputWindow* window);
 
     void releaseTouchedWindowLd();
 
@@ -996,6 +1012,10 @@
     }
 }
 
+nsecs_t NativeInputManager::getKeyRepeatDelay() {
+    return milliseconds_to_nanoseconds(50);
+}
+
 int32_t NativeInputManager::getMaxEventsPerSecond() {
     if (mMaxEventsPerSecond < 0) {
         JNIEnv* env = jniEnv();
@@ -1117,6 +1137,18 @@
                     gInputWindowClassInfo.frameLeft);
             jint frameTop = env->GetIntField(windowObj,
                     gInputWindowClassInfo.frameTop);
+            jint frameRight = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.frameRight);
+            jint frameBottom = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.frameBottom);
+            jint visibleFrameLeft = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.visibleFrameLeft);
+            jint visibleFrameTop = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.visibleFrameTop);
+            jint visibleFrameRight = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.visibleFrameRight);
+            jint visibleFrameBottom = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.visibleFrameBottom);
             jint touchableAreaLeft = env->GetIntField(windowObj,
                     gInputWindowClassInfo.touchableAreaLeft);
             jint touchableAreaTop = env->GetIntField(windowObj,
@@ -1144,6 +1176,12 @@
             outWindow.dispatchingTimeout = dispatchingTimeoutNanos;
             outWindow.frameLeft = frameLeft;
             outWindow.frameTop = frameTop;
+            outWindow.frameRight = frameRight;
+            outWindow.frameBottom = frameBottom;
+            outWindow.visibleFrameLeft = visibleFrameLeft;
+            outWindow.visibleFrameTop = visibleFrameTop;
+            outWindow.visibleFrameRight = visibleFrameRight;
+            outWindow.visibleFrameBottom = visibleFrameBottom;
             outWindow.touchableAreaLeft = touchableAreaLeft;
             outWindow.touchableAreaTop = touchableAreaTop;
             outWindow.touchableAreaRight = touchableAreaRight;
@@ -1417,11 +1455,12 @@
             /* Case 1: ACTION_DOWN */
 
             InputWindow* newTouchedWindow = NULL;
-            mTempTouchedOutsideWindows.clear();
+            mTempTouchedOutsideTargets.clear();
 
             int32_t x = int32_t(motionEvent->getX(0));
             int32_t y = int32_t(motionEvent->getY(0));
             InputWindow* topErrorWindow = NULL;
+            bool obscured = false;
 
             // Traverse windows from front to back to find touched window and outside targets.
             size_t numWindows = mWindows.size();
@@ -1442,13 +1481,17 @@
                         if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
                             if (! screenWasOff || flags & FLAG_TOUCHABLE_WHEN_WAKING) {
                                 newTouchedWindow = window;
+                                obscured = isWindowObscuredLocked(window);
                             }
                             break; // found touched window, exit window loop
                         }
                     }
 
                     if (flags & FLAG_WATCH_OUTSIDE_TOUCH) {
-                        mTempTouchedOutsideWindows.push(window);
+                        OutsideTarget outsideTarget;
+                        outsideTarget.window = window;
+                        outsideTarget.obscured = isWindowObscuredLocked(window);
+                        mTempTouchedOutsideTargets.push(outsideTarget);
                     }
                 }
             }
@@ -1501,6 +1544,7 @@
             releaseTouchedWindowLd();
 
             mTouchedWindow = newTouchedWindow;
+            mTouchedWindowIsObscured = obscured;
 
             if (newTouchedWindow->hasWallpaper) {
                 mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
@@ -1557,21 +1601,31 @@
     if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
         size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
         for (size_t i = 0; i < numWallpaperWindows; i++) {
-            addTarget(mTouchedWallpaperWindows[i], 0, 0, outTargets);
+            addTarget(mTouchedWallpaperWindows[i],
+                    InputTarget::FLAG_WINDOW_IS_OBSCURED, 0, outTargets);
         }
 
-        size_t numOutsideWindows = mTempTouchedOutsideWindows.size();
-        for (size_t i = 0; i < numOutsideWindows; i++) {
-            addTarget(mTempTouchedOutsideWindows[i], InputTarget::FLAG_OUTSIDE, 0, outTargets);
+        size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
+        for (size_t i = 0; i < numOutsideTargets; i++) {
+            const OutsideTarget& outsideTarget = mTempTouchedOutsideTargets[i];
+            int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
+            if (outsideTarget.obscured) {
+                outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+            }
+            addTarget(outsideTarget.window, outsideTargetFlags, 0, outTargets);
         }
 
-        addTarget(mTouchedWindow, InputTarget::FLAG_SYNC,
+        int32_t targetFlags = InputTarget::FLAG_SYNC;
+        if (mTouchedWindowIsObscured) {
+            targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+        }
+        addTarget(mTouchedWindow, targetFlags,
                 anrTimer.getTimeSpentWaitingForApplication(), outTargets);
         outTouchedWindow = mTouchedWindow;
     } else {
         outTouchedWindow = NULL;
     }
-    mTempTouchedOutsideWindows.clear();
+    mTempTouchedOutsideTargets.clear();
 
     // Check injection permission once and for all.
     if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
@@ -1616,6 +1670,7 @@
 
 void NativeInputManager::releaseTouchedWindowLd() {
     mTouchedWindow = NULL;
+    mTouchedWindowIsObscured = false;
     mTouchedWallpaperWindows.clear();
 }
 
@@ -1661,6 +1716,20 @@
     return true;
 }
 
+bool NativeInputManager::isWindowObscuredLocked(const InputWindow* window) {
+    size_t numWindows = mWindows.size();
+    for (size_t i = 0; i < numWindows; i++) {
+        const InputWindow* other = & mWindows.itemAt(i);
+        if (other == window) {
+            break;
+        }
+        if (other->visible && window->visibleFrameIntersects(other)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
         int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
@@ -1935,12 +2004,17 @@
     for (size_t i = 0; i < mWindows.size(); i++) {
         dump.appendFormat("  windows[%d]: '%s', paused=%d, hasFocus=%d, hasWallpaper=%d, "
                 "visible=%d, flags=0x%08x, type=0x%08x, "
-                "frame=[%d,%d], touchableArea=[%d,%d][%d,%d], "
+                "frame=[%d,%d][%d,%d], "
+                "visibleFrame=[%d,%d][%d,%d], "
+                "touchableArea=[%d,%d][%d,%d], "
                 "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
                 i, mWindows[i].inputChannel->getName().string(),
                 mWindows[i].paused, mWindows[i].hasFocus, mWindows[i].hasWallpaper,
                 mWindows[i].visible, mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
                 mWindows[i].frameLeft, mWindows[i].frameTop,
+                mWindows[i].frameRight, mWindows[i].frameBottom,
+                mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop,
+                mWindows[i].visibleFrameRight, mWindows[i].visibleFrameBottom,
                 mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
                 mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
                 mWindows[i].ownerPid, mWindows[i].ownerUid,
@@ -1955,6 +2029,20 @@
 
 // ----------------------------------------------------------------------------
 
+bool NativeInputManager::InputWindow::visibleFrameIntersects(const InputWindow* other) const {
+    return visibleFrameRight > other->visibleFrameLeft
+        && visibleFrameLeft < other->visibleFrameRight
+        && visibleFrameBottom > other->visibleFrameTop
+        && visibleFrameTop < other->visibleFrameBottom;
+}
+
+bool NativeInputManager::InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
+    return x >= touchableAreaLeft && x <= touchableAreaRight
+            && y >= touchableAreaTop && y <= touchableAreaBottom;
+}
+
+// ----------------------------------------------------------------------------
+
 NativeInputManager::ANRTimer::ANRTimer() :
         mBudget(APPLICATION), mStartTime(now()), mFrozen(false), mPausedWindow(NULL) {
 }
@@ -2507,6 +2595,24 @@
     GET_FIELD_ID(gInputWindowClassInfo.frameTop, gInputWindowClassInfo.clazz,
             "frameTop", "I");
 
+    GET_FIELD_ID(gInputWindowClassInfo.frameRight, gInputWindowClassInfo.clazz,
+            "frameRight", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.frameBottom, gInputWindowClassInfo.clazz,
+            "frameBottom", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.visibleFrameLeft, gInputWindowClassInfo.clazz,
+            "visibleFrameLeft", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.visibleFrameTop, gInputWindowClassInfo.clazz,
+            "visibleFrameTop", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.visibleFrameRight, gInputWindowClassInfo.clazz,
+            "visibleFrameRight", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.visibleFrameBottom, gInputWindowClassInfo.clazz,
+            "visibleFrameBottom", "I");
+
     GET_FIELD_ID(gInputWindowClassInfo.touchableAreaLeft, gInputWindowClassInfo.clazz,
             "touchableAreaLeft", "I");
 
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 4f2d90f..e37733c 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -141,7 +141,7 @@
         Uri uri = intent.getData();
         String scheme = uri.getScheme();
 
-        if (scheme.equals("tel")) {
+        if (scheme.equals("tel") || scheme.equals("sip")) {
             return uri.getSchemeSpecificPart();
         }
 
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 12df44e..c1232e8 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -1108,14 +1108,21 @@
      */
 
     /**
-     * @return list of ringing calls
+     * @return list of all ringing calls
      */
     public ArrayList<Call> getRingingCalls() {
         return mRingingCalls;
     }
 
     /**
-     * @return list of background calls
+     * @return list of all foreground calls
+     */
+    public ArrayList<Call> getForegroundCalls() {
+        return mForegroundCalls;
+    }
+
+    /**
+     * @return list of all background calls
      */
     public ArrayList<Call> getBackgroundCalls() {
         return mBackgroundCalls;
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 36a2fcf..78ce473 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -285,7 +285,7 @@
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
         editor.putBoolean(DNS_SERVER_CHECK_DISABLED_KEY, b);
-        editor.commit();
+        editor.apply();
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 1a0dd89..8c2280b 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -1298,10 +1298,18 @@
     public void
     setupDataCall(String radioTechnology, String profile, String apn,
             String user, String password, String authType, Message result) {
+        setupDataCallWithProtocol(radioTechnology, profile, apn, user, password,
+                              authType, "IP", result);
+    }
+
+    public void
+    setupDataCallWithProtocol(String radioTechnology, String profile,
+            String apn, String user, String password, String authType,
+            String protocolType, Message result) {
         RILRequest rr
                 = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
 
-        rr.mp.writeInt(6);
+        rr.mp.writeInt(7);
 
         rr.mp.writeString(radioTechnology);
         rr.mp.writeString(profile);
@@ -1309,11 +1317,12 @@
         rr.mp.writeString(user);
         rr.mp.writeString(password);
         rr.mp.writeString(authType);
+        rr.mp.writeString(protocolType);
 
         if (RILJ_LOGD) riljLog(rr.serialString() + "> "
                 + requestToString(rr.mRequest) + " " + radioTechnology + " "
                 + profile + " " + apn + " " + user + " "
-                + password + " " + authType);
+                + password + " " + authType + " " + protocolType);
 
         send(rr);
     }
@@ -2901,7 +2910,11 @@
             dataCall.active = p.readInt();
             dataCall.type = p.readString();
             dataCall.apn = p.readString();
-            dataCall.address = p.readString();
+            String address = p.readString();
+            if (address != null) {
+                address = address.split(" ")[0];
+            }
+            dataCall.address = address;
 
             response.add(dataCall);
         }
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index f1a7107..3a7ce47 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -32,9 +32,11 @@
 import android.database.SQLException;
 import android.net.Uri;
 import android.os.AsyncResult;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.StatFs;
 import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
 import android.provider.Settings;
@@ -240,11 +242,9 @@
 
         // Register for device storage intents.  Use these to notify the RIL
         // that storage for SMS is or is not available.
-        // TODO: Revisit this for a later release.  Storage reporting should
-        // rely more on application indication.
         IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
-        filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
+        filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL);
+        filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
         mContext.registerReceiver(mResultReceiver, filter);
     }
 
@@ -966,10 +966,10 @@
     private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) {
+            if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) {
                 mStorageAvailable = false;
                 mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
-            } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) {
+            } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) {
                 mStorageAvailable = true;
                 mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
             } else {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 8d77f76..6e53ec5 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -101,7 +101,6 @@
     CdmaCallTracker mCT;
     CdmaSMSDispatcher mSMS;
     CdmaServiceStateTracker mSST;
-    RuimFileHandler mRuimFileHandler;
     RuimRecords mRuimRecords;
     RuimCard mRuimCard;
     ArrayList <CdmaMmiCode> mPendingMmis = new ArrayList<CdmaMmiCode>();
@@ -159,7 +158,7 @@
         mDataConnection = new CdmaDataConnectionTracker (this);
         mRuimCard = new RuimCard(this);
         mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this);
-        mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this);
+        mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this, mSMS);
         mSubInfo = new PhoneSubInfo(this);
         mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML);
         mCcatService = CatService.getInstance(mCM, mRuimRecords, mContext,
@@ -1406,7 +1405,7 @@
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
         editor.putString(VM_NUMBER_CDMA, number);
-        editor.commit();
+        editor.apply();
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 53d0f20..37a33bc 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -129,7 +129,7 @@
                     mPhone.getContext());
             SharedPreferences.Editor editor = sp.edit();
             editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
-            editor.commit();
+            editor.apply();
             ((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount);
             handled = true;
         } else if (((SmsEnvelope.TELESERVICE_WMT == teleService) ||
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index a9df375..d84b6ab 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -27,6 +27,7 @@
 import com.android.internal.telephony.IccSmsInterfaceManager;
 import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsRawData;
 
 import java.util.ArrayList;
@@ -80,9 +81,9 @@
         }
     };
 
-    public RuimSmsInterfaceManager(CDMAPhone phone) {
+    public RuimSmsInterfaceManager(CDMAPhone phone, SMSDispatcher dispatcher) {
         super(phone);
-        mDispatcher = new CdmaSMSDispatcher(phone);
+        mDispatcher = dispatcher;
     }
 
     public void dispose() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 7331e05..30a6324 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -117,10 +117,6 @@
     Thread debugPortThread;
     ServerSocket debugSocket;
 
-    private int mReportedRadioResets;
-    private int mReportedAttemptedConnects;
-    private int mReportedSuccessfulConnects;
-
     private String mImei;
     private String mImeiSv;
     private String mVmNumber;
@@ -151,7 +147,7 @@
         mSimCard = new SimCard(this);
         if (!unitTestMode) {
             mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
-            mSimSmsIntManager = new SimSmsInterfaceManager(this);
+            mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS);
             mSubInfo = new PhoneSubInfo(this);
         }
         mStkService = CatService.getInstance(mCM, mSIMRecords, mContext,
@@ -809,7 +805,7 @@
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
         editor.putString(VM_NUMBER, number);
-        editor.commit();
+        editor.apply();
         setVmSimImsi(getSubscriberId());
     }
 
@@ -832,7 +828,7 @@
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
         editor.putString(VM_SIM_IMSI, imsi);
-        editor.commit();
+        editor.apply();
     }
 
     public String getVoiceMailAlphaTag() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index f000d79..aab359f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -25,6 +25,7 @@
 import com.android.internal.telephony.IccConstants;
 import com.android.internal.telephony.IccSmsInterfaceManager;
 import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsRawData;
 
 import java.util.ArrayList;
@@ -78,9 +79,9 @@
         }
     };
 
-    public SimSmsInterfaceManager(GSMPhone phone) {
+    public SimSmsInterfaceManager(GSMPhone phone, SMSDispatcher dispatcher) {
         super(phone);
-        mDispatcher = new GsmSMSDispatcher(phone);
+        mDispatcher = dispatcher;
     }
 
     public void dispose() {
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 9a45d3a..b4efeea 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -114,6 +114,10 @@
         return mProfile.getUriString();
     }
 
+    public boolean equals(SipPhone phone) {
+        return getSipUri().equals(phone.getSipUri());
+    }
+
     public boolean canTake(Object incomingCall) {
         synchronized (SipPhone.class) {
             if (!(incomingCall instanceof SipAudioCall)) return false;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
index e742887..5f26af4 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -175,8 +175,7 @@
     }
 
     public int getPhoneType() {
-        // FIXME: add SIP phone type
-        return Phone.PHONE_TYPE_GSM;
+        return Phone.PHONE_TYPE_SIP;
     }
 
     public SignalStrength getSignalStrength() {
diff --git a/test-runner/src/android/test/ProviderTestCase2.java b/test-runner/src/android/test/ProviderTestCase2.java
index 64d11c5..2811b0d 100644
--- a/test-runner/src/android/test/ProviderTestCase2.java
+++ b/test-runner/src/android/test/ProviderTestCase2.java
@@ -74,7 +74,7 @@
     private IsolatedContext mProviderContext;
     private MockContentResolver mResolver;
 
-       private class MockContext2 extends MockContext {
+    private class MockContext2 extends MockContext {
 
         @Override
         public Resources getResources() {
@@ -87,6 +87,11 @@
             // one created through the regular Context
             return getContext().getDir("mockcontext2_" + name, mode);
         }
+
+        @Override
+        public Context getApplicationContext() {
+            return this;
+        }
     }
     /**
      * Constructor.
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 4cff3de..65da210 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -81,8 +81,10 @@
         // http://b/issue?id=2889595
         "ietestcenter/Javascript/15.4.4.15-3-29.html", // hangs the layout tests
         // http://b/issue?id=2889596
-        "ietestcenter/Javascript/15.4.4.15-3-8.html" // hangs the layout tests
+        "ietestcenter/Javascript/15.4.4.15-3-8.html", // hangs the layout tests
         // http://b/issue?id=2889598
+        "http/tests/xmlhttprequest/simple-cross-origin-progress-events.html", // runs webcore into bad state
+        // http://b/2982500
     };
 
     static void fillIgnoreResultList() {
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
index dd0c4e9..b4dc190 100644
--- a/tests/DumpRenderTree2/AndroidManifest.xml
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -35,7 +35,8 @@
              It can lead to some weird behaviour in the future. -->
         <activity android:name=".TestsListActivity"
                   android:label="Tests' list activity"
-                  android:launchMode="singleTask">
+                  android:launchMode="singleTask"
+                  android:configChanges="orientation">
         </activity>
 
         <activity android:name=".LayoutTestsExecutor"
diff --git a/tests/DumpRenderTree2/assets/run_apache2.py b/tests/DumpRenderTree2/assets/run_apache2.py
index 5764f0e..5d1e66c 100755
--- a/tests/DumpRenderTree2/assets/run_apache2.py
+++ b/tests/DumpRenderTree2/assets/run_apache2.py
@@ -76,6 +76,8 @@
   directives += " -c \"Alias /LayoutTests " + layout_tests_path + "\""
   directives += " -c \"Alias /WebKitTools/DumpRenderTree/android " + \
     os.path.join(webkit_path, "WebKitTools", "DumpRenderTree", "android") + "\""
+  directives += " -c \"Alias /ThirdPartyProject.prop " + \
+    os.path.join(webkit_path, "ThirdPartyProject.prop") + "\""
 
   # This directive is commented out in apache2-debian-httpd.conf for some reason
   # However, it is useful to browse through tests in the browser, so it's added here.
@@ -90,7 +92,8 @@
     "\""
   directives += " -c \"User ${APACHE_RUN_USER}\""
   directives += " -c \"Group ${APACHE_RUN_GROUP}\""
-  directives += " -C \"TypesConfig " + os.path.join("/etc", "mime.types") + "\""
+  directives += " -C \"TypesConfig " + \
+    os.path.join(android_tree_root, http_conf_path, "mime.types") + "\""
   conf_file_cmd = " -f " + \
     os.path.join(android_tree_root, http_conf_path, "apache2-debian-httpd.conf")
 
diff --git a/tests/DumpRenderTree2/res/menu/gui_menu.xml b/tests/DumpRenderTree2/res/menu/gui_menu.xml
new file mode 100644
index 0000000..a5b2b65
--- /dev/null
+++ b/tests/DumpRenderTree2/res/menu/gui_menu.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/run_all"
+          android:title="@string/run_all_tests" />
+</menu>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/res/values/strings.xml b/tests/DumpRenderTree2/res/values/strings.xml
index 5fd1eb9..0496404 100644
--- a/tests/DumpRenderTree2/res/values/strings.xml
+++ b/tests/DumpRenderTree2/res/values/strings.xml
@@ -25,4 +25,6 @@
     <string name="dialog_progress_msg">Please wait...</string>
 
     <string name="runner_preloading_title">Preloading tests...</string>
+
+    <string name="run_all_tests">Run all tests in the current directory</string>
 </resources>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java
index d68930c..7cbb397 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java
@@ -92,8 +92,16 @@
 
     public abstract void setExpectedImageResult(byte[] expectedResult);
 
+    public abstract void setExpectedImageResultPath(String relativePath);
+
+    public abstract String getExpectedImageResultPath();
+
     public abstract void setExpectedTextResult(String expectedResult);
 
+    public abstract void setExpectedTextResultPath(String relativePath);
+
+    public abstract String getExpectedTextResultPath();
+
     /**
      * Returns result's image data that can be written to the disk. It can be null
      * if there is an error of some sort or for example the test times out.
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
index a793dab..31da776 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
@@ -22,6 +22,8 @@
 
 /**
  * A dummy class representing test that crashed.
+ *
+ * TODO: All the methods regarding expected results need implementing.
  */
 public class CrashedDummyResult extends AbstractResult {
     String mRelativePath;
@@ -82,4 +84,26 @@
     public void setExpectedTextResult(String expectedResult) {
         /** TODO */
     }
+
+    @Override
+    public String getExpectedImageResultPath() {
+        /** TODO */
+        return null;
+    }
+
+    @Override
+    public String getExpectedTextResultPath() {
+        /** TODO */
+        return null;
+    }
+
+    @Override
+    public void setExpectedImageResultPath(String relativePath) {
+        /** TODO */
+    }
+
+    @Override
+    public void setExpectedTextResultPath(String relativePath) {
+        /** TODO */
+    }
 }
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
index 4f2fc77..4ab76e3 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
@@ -48,12 +48,12 @@
     private static final String HTTP_TESTS_PATH = "http/tests/";
     private static final String SSL_PATH = "ssl/";
 
-    private static final String TOKEN_SKIP = "SKIP";
-    private static final String TOKEN_IGNORE_RESULT = "IGNORE_RESULT";
+    private static final String TOKEN_CRASH = "CRASH";
+    private static final String TOKEN_FAIL = "FAIL";
     private static final String TOKEN_SLOW = "SLOW";
 
-    private final Set<String> mSkipList = new HashSet<String>();
-    private final Set<String> mIgnoreResultList = new HashSet<String>();
+    private final Set<String> mCrashList = new HashSet<String>();
+    private final Set<String> mFailList = new HashSet<String>();
     private final Set<String> mSlowList = new HashSet<String>();
 
     private final String mRootDirPath;
@@ -91,7 +91,6 @@
                 String[] parts;
                 String path;
                 Set<String> tokens;
-                Boolean skipped;
                 while (true) {
                     line = bufferedReader.readLine();
                     if (line == null) {
@@ -122,21 +121,16 @@
                     tokens = new HashSet<String>(Arrays.asList(parts[1].split("\\s", 0)));
 
                     /** Chose the right collections to add to */
-                    skipped = false;
-                    if (tokens.contains(TOKEN_SKIP)) {
-                        mSkipList.add(path);
-                        skipped = true;
-                    }
+                    if (tokens.contains(TOKEN_CRASH)) {
+                        mCrashList.add(path);
 
-                    /** If test is on skip list we ignore any further options */
-                    if (skipped) {
+                        /** If test is on skip list we ignore any further options */
                         continue;
                     }
 
-                    if (tokens.contains(TOKEN_IGNORE_RESULT)) {
-                        mIgnoreResultList.add(path);
+                    if (tokens.contains(TOKEN_FAIL)) {
+                        mFailList.add(path);
                     }
-
                     if (tokens.contains(TOKEN_SLOW)) {
                         mSlowList.add(path);
                     }
@@ -157,7 +151,7 @@
     }
 
     /**
-     * Checks if test is supposed to be skipped.
+     * Checks if test is expected to crash.
      *
      * <p>
      * Path given should relative within LayoutTests folder, e.g. fast/dom/foo.html
@@ -166,9 +160,9 @@
      *            - a relative path within LayoutTests folder
      * @return if the test is supposed to be skipped
      */
-    public boolean isSkip(String testPath) {
+    public boolean isCrash(String testPath) {
         for (String prefix : getPrefixes(testPath)) {
-            if (mSkipList.contains(prefix)) {
+            if (mCrashList.contains(prefix)) {
                 return true;
             }
         }
@@ -177,18 +171,18 @@
     }
 
     /**
-     * Checks if test result is supposed to be ignored.
+     * Checks if test result is supposed to be "failed".
      *
      * <p>
      * Path given should relative within LayoutTests folder, e.g. fast/dom/foo.html
      *
      * @param testPath
      *            - a relative path within LayoutTests folder
-     * @return if the test result is supposed to be ignored
+     * @return if the test result is supposed to be "failed"
      */
-    public boolean isIgnoreRes(String testPath) {
+    public boolean isFail(String testPath) {
         for (String prefix : getPrefixes(testPath)) {
-            if (mIgnoreResultList.contains(prefix)) {
+            if (mFailList.contains(prefix)) {
                 return true;
             }
         }
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index e62335e..0bd2302 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -29,6 +29,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.PowerManager.WakeLock;
 import android.util.Log;
@@ -44,6 +45,7 @@
 import android.webkit.WebStorage.QuotaUpdater;
 
 import java.io.File;
+import java.lang.Thread.UncaughtExceptionHandler;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.HashMap;
@@ -256,6 +258,32 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        /**
+         * It detects the crash by catching all the uncaught exceptions. However, we
+         * still have to kill the process, because after catching the exception the
+         * activity remains in a strange state, where intents don't revive it.
+         * However, we send the message to the service to speed up the rebooting
+         * (we don't have to wait for time-out to kick in).
+         */
+        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+            @Override
+            public void uncaughtException(Thread thread, Throwable e) {
+                Log.w(LOG_TAG,
+                        "onTestCrashed(): " + mCurrentTestRelativePath + " thread=" + thread, e);
+
+                try {
+                    Message serviceMsg =
+                            Message.obtain(null, ManagerService.MSG_CURRENT_TEST_CRASHED);
+
+                    mManagerServiceMessenger.send(serviceMsg);
+                } catch (RemoteException e2) {
+                    Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e2);
+                }
+
+                Process.killProcess(Process.myPid());
+            }
+        });
+
         requestWindowFeature(Window.FEATURE_PROGRESS);
 
         Intent intent = getIntent();
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
index c745593..d9da672 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -38,7 +38,7 @@
 
     private static final String LOG_TAG = "ManagerService";
 
-    private static final int MSG_TEST_CRASHED = 0;
+    private static final int MSG_CRASH_TIMEOUT_EXPIRED = 0;
 
     private static final int CRASH_TIMEOUT_MS = 20 * 1000;
 
@@ -72,6 +72,7 @@
     static final int MSG_PROCESS_ACTUAL_RESULTS = 0;
     static final int MSG_ALL_TESTS_FINISHED = 1;
     static final int MSG_FIRST_TEST = 2;
+    static final int MSG_CURRENT_TEST_CRASHED = 3;
 
     /**
      * This handler is purely for IPC. It is used to create mMessenger
@@ -92,6 +93,11 @@
                     onActualResultsObtained(msg.getData());
                     break;
 
+                case MSG_CURRENT_TEST_CRASHED:
+                    mCrashMessagesHandler.removeMessages(MSG_CRASH_TIMEOUT_EXPIRED);
+                    onTestCrashed();
+                    break;
+
                 case MSG_ALL_TESTS_FINISHED:
                     mSummarizer.setTestsRelativePath(mAllTestsRelativePath);
                     mSummarizer.summarize();
@@ -110,7 +116,7 @@
     private Handler mCrashMessagesHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
-            if (msg.what == MSG_TEST_CRASHED) {
+            if (msg.what == MSG_CRASH_TIMEOUT_EXPIRED) {
                 onTestCrashed();
             }
         }
@@ -122,6 +128,13 @@
     private String mCurrentlyRunningTest;
     private int mCurrentlyRunningTestIndex;
 
+    /**
+     * These are implementation details of getExpectedResultPath() used to reduce the number
+     * of requests required to the host server.
+     */
+    private String mLastExpectedResultPathRequested;
+    private String mLastExpectedResultPathFetched;
+
     private String mAllTestsRelativePath;
 
     @Override
@@ -145,7 +158,7 @@
     }
 
     private void onActualResultsObtained(Bundle bundle) {
-        mCrashMessagesHandler.removeMessages(MSG_TEST_CRASHED);
+        mCrashMessagesHandler.removeMessages(MSG_CRASH_TIMEOUT_EXPIRED);
         ensureNextTestSetup(bundle.getString("nextTest"), bundle.getInt("testIndex") + 1);
 
         AbstractResult results =
@@ -163,7 +176,7 @@
 
         mCurrentlyRunningTest = nextTest;
         mCurrentlyRunningTestIndex = index;
-        mCrashMessagesHandler.sendEmptyMessageDelayed(MSG_TEST_CRASHED, CRASH_TIMEOUT_MS);
+        mCrashMessagesHandler.sendEmptyMessageDelayed(MSG_CRASH_TIMEOUT_EXPIRED, CRASH_TIMEOUT_MS);
     }
 
     /**
@@ -188,7 +201,9 @@
     private void handleResults(AbstractResult results) {
         String relativePath = results.getRelativePath();
         results.setExpectedTextResult(getExpectedTextResult(relativePath));
+        results.setExpectedTextResultPath(getExpectedTextResultPath(relativePath));
         results.setExpectedImageResult(getExpectedImageResult(relativePath));
+        results.setExpectedImageResultPath(getExpectedImageResultPath(relativePath));
 
         dumpActualTextResult(results);
         dumpActualImageResult(results);
@@ -221,7 +236,7 @@
                 actualImageResult, false);
     }
 
-    public static String getExpectedTextResult(String relativePath) {
+    public String getExpectedTextResult(String relativePath) {
         byte[] result = getExpectedResult(relativePath, TEXT_RESULT_EXTENSION);
         if (result != null) {
             return new String(result);
@@ -229,13 +244,14 @@
         return null;
     }
 
-    public static byte[] getExpectedImageResult(String relativePath) {
+    public byte[] getExpectedImageResult(String relativePath) {
         return getExpectedResult(relativePath, IMAGE_RESULT_EXTENSION);
     }
 
-    private static byte[] getExpectedResult(String relativePath, String extension) {
+    private byte[] getExpectedResult(String relativePath, String extension) {
         String originalRelativePath =
                 FileFilter.setPathEnding(relativePath, "-expected." + extension);
+        mLastExpectedResultPathRequested = originalRelativePath;
 
         byte[] bytes = null;
         List<String> locations = EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES;
@@ -246,6 +262,25 @@
             bytes = FsUtils.readDataFromUrl(FileFilter.getUrl(relativePath));
         }
 
+        mLastExpectedResultPathFetched = relativePath;
         return bytes;
     }
+
+    private String getExpectedTextResultPath(String relativePath) {
+        return getExpectedResultPath(relativePath, TEXT_RESULT_EXTENSION);
+    }
+
+    private String getExpectedImageResultPath(String relativePath) {
+        return getExpectedResultPath(relativePath, IMAGE_RESULT_EXTENSION);
+    }
+
+    private String getExpectedResultPath(String relativePath, String extension) {
+        String originalRelativePath =
+            FileFilter.setPathEnding(relativePath, "-expected." + extension);
+        if (!originalRelativePath.equals(mLastExpectedResultPathRequested)) {
+            getExpectedResult(relativePath, extension);
+        }
+
+        return mLastExpectedResultPathFetched;
+    }
 }
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
index 7bbbc05..0efb78e 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -27,6 +27,7 @@
 
 import java.io.File;
 import java.net.MalformedURLException;
+import java.net.URI;
 import java.net.URL;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -64,7 +65,8 @@
             "       font-size: 14px;" +
             "       color: black;" +
             "       text-decoration: none;" +
-            "       margin-bottom: 4px;}" +
+            "       margin-top: 4px;" +
+            "       margin-bottom: 2px;}" +
             "h3 a span.path {" +
             "       text-decoration: underline;}" +
             "h3 span.tri {" +
@@ -76,6 +78,15 @@
             "       color: #8ee100;" +
             "       float: left;" +
             "       width: 20px;}" +
+            "span.source {" +
+            "       display: block;" +
+            "       font-size: 10px;" +
+            "       color: #888;" +
+            "       margin-left: 20px;" +
+            "       margin-bottom: 1px;}" +
+            "span.source a {" +
+            "       font-size: 10px;" +
+            "       color: #888;}" +
             "h3 img {" +
             "       width: 8px;" +
             "       margin-right: 4px;}" +
@@ -183,9 +194,10 @@
     private static final String TXT_SUMMARY_RELATIVE_PATH = "summary.txt";
 
     private int mCrashedTestsCount = 0;
-    private List<AbstractResult> mFailedNotIgnoredTests = new ArrayList<AbstractResult>();
-    private List<AbstractResult> mIgnoredTests = new ArrayList<AbstractResult>();
-    private List<String> mPassedNotIgnoredTests = new ArrayList<String>();
+    private List<AbstractResult> mUnexpectedFailures = new ArrayList<AbstractResult>();
+    private List<AbstractResult> mExpectedFailures = new ArrayList<AbstractResult>();
+    private List<AbstractResult> mExpectedPasses = new ArrayList<AbstractResult>();
+    private List<AbstractResult> mUnexpectedPasses = new ArrayList<AbstractResult>();
 
     private FileFilter mFileFilter;
     private String mResultsRootDirPath;
@@ -199,6 +211,11 @@
         mResultsRootDirPath = resultsRootDirPath;
     }
 
+    public static URI getDetailsUri() {
+        return new File(ManagerService.RESULTS_ROOT_DIR_PATH + File.separator +
+                HTML_DETAILS_RELATIVE_PATH).toURI();
+    }
+
     public void appendTest(AbstractResult result) {
         String relativePath = result.getRelativePath();
 
@@ -206,12 +223,18 @@
             mCrashedTestsCount++;
         }
 
-        if (mFileFilter.isIgnoreRes(relativePath)) {
-            mIgnoredTests.add(result);
-        } else if (result.getResultCode() == AbstractResult.ResultCode.PASS) {
-            mPassedNotIgnoredTests.add(relativePath);
+        if (result.getResultCode() == AbstractResult.ResultCode.PASS) {
+            if (mFileFilter.isFail(relativePath)) {
+                mUnexpectedPasses.add(result);
+            } else {
+                mExpectedPasses.add(result);
+            }
         } else {
-            mFailedNotIgnoredTests.add(result);
+            if (mFileFilter.isFail(relativePath)) {
+                mExpectedFailures.add(result);
+            } else {
+                mUnexpectedFailures.add(result);
+            }
         }
     }
 
@@ -226,9 +249,9 @@
 
     public void reset() {
         mCrashedTestsCount = 0;
-        mFailedNotIgnoredTests.clear();
-        mIgnoredTests.clear();
-        mPassedNotIgnoredTests.clear();
+        mUnexpectedFailures.clear();
+        mExpectedFailures.clear();
+        mExpectedPasses.clear();
         mDate = new Date();
     }
 
@@ -240,15 +263,17 @@
         txt.append("Date: " + dateFormat.format(mDate) + "\n");
         txt.append("Build fingerprint: " + Build.FINGERPRINT + "\n");
         txt.append("WebKit version: " + getWebKitVersionFromUserAgentString() + "\n");
+        txt.append("WebKit revision: " + getWebKitRevision() + "\n");
 
         txt.append("TOTAL:   " + getTotalTestCount() + "\n");
         if (mCrashedTestsCount > 0) {
             txt.append("CRASHED (total among all tests): " + mCrashedTestsCount + "\n");
             txt.append("-------------");
         }
-        txt.append("FAILED:  " + mFailedNotIgnoredTests.size() + "\n");
-        txt.append("IGNORED: " + mIgnoredTests.size() + "\n");
-        txt.append("PASSED:  " + mPassedNotIgnoredTests.size() + "\n");
+        txt.append("UNEXPECTED FAILURES: " + mUnexpectedFailures.size() + "\n");
+        txt.append("UNEXPECTED PASSES:   " + mUnexpectedPasses.size() + "\n");
+        txt.append("EXPECTED FAILURES:   " + mExpectedFailures.size() + "\n");
+        txt.append("EXPECTED PASSES:     " + mExpectedPasses.size() + "\n");
 
         FsUtils.writeDataToStorage(new File(mResultsRootDirPath, TXT_SUMMARY_RELATIVE_PATH),
                 txt.toString().getBytes(), false);
@@ -264,11 +289,13 @@
 
         createTopSummaryTable(html);
 
-        createResultsListWithDiff(html, "Failed", mFailedNotIgnoredTests);
+        createResultsListWithDiff(html, "Unexpected failures", mUnexpectedFailures);
 
-        createResultsListWithDiff(html, "Ignored", mIgnoredTests);
+        createResultsListNoDiff(html, "Unexpected passes", mUnexpectedPasses);
 
-        createResultsListNoDiff(html, "Passed", mPassedNotIgnoredTests);
+        createResultsListWithDiff(html, "Expected failures", mExpectedFailures);
+
+        createResultsListNoDiff(html, "Expected passes", mExpectedPasses);
 
         html.append("</body></html>");
 
@@ -277,9 +304,10 @@
     }
 
     private int getTotalTestCount() {
-        return mFailedNotIgnoredTests.size() +
-                mPassedNotIgnoredTests.size() +
-                mIgnoredTests.size();
+        return mUnexpectedFailures.size() +
+                mUnexpectedPasses.size() +
+                mExpectedPasses.size() +
+                mExpectedFailures.size();
     }
 
     private String getWebKitVersionFromUserAgentString() {
@@ -295,6 +323,23 @@
         return "unknown";
     }
 
+    private String getWebKitRevision() {
+        URL url = null;
+        try {
+            url = new URL(ForwarderManager.getHostSchemePort(false) + "ThirdPartyProject.prop");
+        } catch (MalformedURLException e) {
+            assert false;
+        }
+
+        String thirdPartyProjectContents = new String(FsUtils.readDataFromUrl(url));
+        Matcher matcher = Pattern.compile("^version=([0-9]+)", Pattern.MULTILINE).matcher(
+                thirdPartyProjectContents);
+        if (matcher.find()) {
+            return matcher.group(1);
+        }
+        return "unknown";
+    }
+
     private void createTopSummaryTable(StringBuilder html) {
         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
         html.append("<h1>" + mTestsRelativePath + "</h1>");
@@ -302,12 +347,19 @@
         html.append("<h3>" + "Build fingerprint: " + Build.FINGERPRINT + "</h3>");
         html.append("<h3>" + "WebKit version: " + getWebKitVersionFromUserAgentString() + "</h3>");
 
+        String webkitRevision = getWebKitRevision();
+        html.append("<h3>" + "WebKit revision: ");
+        html.append("<a href=\"http://trac.webkit.org/browser/trunk?rev=" + webkitRevision +
+                "\" target=\"_blank\"><span class=\"path\">" + webkitRevision + "</span></a>");
+        html.append("</h3>");
+
         html.append("<table class=\"summary\">");
         createSummaryTableRow(html, "TOTAL", getTotalTestCount());
         createSummaryTableRow(html, "CRASHED", mCrashedTestsCount);
-        createSummaryTableRow(html, "FAILED", mFailedNotIgnoredTests.size());
-        createSummaryTableRow(html, "IGNORED", mIgnoredTests.size());
-        createSummaryTableRow(html, "PASSED", mPassedNotIgnoredTests.size());
+        createSummaryTableRow(html, "UNEXPECTED FAILURES", mUnexpectedFailures.size());
+        createSummaryTableRow(html, "UNEXPECTED PASSES", mUnexpectedPasses.size());
+        createSummaryTableRow(html, "EXPECTED FAILURES", mExpectedFailures.size());
+        createSummaryTableRow(html, "EXPECTED PASSES", mExpectedPasses.size());
         html.append("</table>");
     }
 
@@ -329,25 +381,21 @@
         for (AbstractResult result : resultsList) {
             relativePath = result.getRelativePath();
             resultCode = result.getResultCode();
+            assert resultCode != AbstractResult.ResultCode.PASS : "resultCode=" + resultCode;
 
             html.append("<h3>");
 
-            if (resultCode == AbstractResult.ResultCode.PASS) {
-                html.append("<span class=\"sqr\">&#x25a0; </span>");
-                html.append("<span class=\"path\">" + relativePath + "</span>");
-            } else {
-                /**
-                 * Technically, two different paths could end up being the same, because
-                 * ':' is a valid  character in a path. However, it is probably not going
-                 * to cause any problems in this case
-                 */
-                id = relativePath.replace(File.separator, ":");
-                html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
-                html.append("return false;\">");
-                html.append("<span class=\"tri\" id=\"tri." + id + "\">&#x25b6; </span>");
-                html.append("<span class=\"path\">" + relativePath + "</span>");
-                html.append("</a>");
-            }
+            /**
+             * Technically, two different paths could end up being the same, because
+             * ':' is a valid  character in a path. However, it is probably not going
+             * to cause any problems in this case
+             */
+            id = relativePath.replace(File.separator, ":");
+            html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
+            html.append("return false;\">");
+            html.append("<span class=\"tri\" id=\"tri." + id + "\">&#x25b6; </span>");
+            html.append("<span class=\"path\">" + relativePath + "</span>");
+            html.append("</a>");
 
             html.append(" <span class=\"listItem " + resultCode.name() + "\">");
             html.append(resultCode.toString());
@@ -368,38 +416,60 @@
             }
 
             html.append("</h3>");
+            appendExpectedResultsSources(result, html);
 
-            if (resultCode != AbstractResult.ResultCode.PASS) {
-                html.append("<div class=\"diff\" style=\"display: none;\" id=\"" + id + "\">");
-                html.append(result.getDiffAsHtml());
-                html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
-                html.append("return false;\">Hide</a>");
-                html.append(" | ");
-                html.append("<a href=\"" + getViewSourceUrl(relativePath).toString() + "\"");
-                html.append(" target=\"_blank\">Show source</a>");
-                html.append("</div>");
-            }
+            html.append("<div class=\"diff\" style=\"display: none;\" id=\"" + id + "\">");
+            html.append(result.getDiffAsHtml());
+            html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
+            html.append("return false;\">Hide</a>");
+            html.append(" | ");
+            html.append("<a href=\"" + getViewSourceUrl(relativePath).toString() + "\"");
+            html.append(" target=\"_blank\">Show source</a>");
+            html.append("</div>");
 
             html.append("<div class=\"space\"></div>");
         }
     }
 
     private void createResultsListNoDiff(StringBuilder html, String title,
-            List<String> resultsList) {
+            List<AbstractResult> resultsList) {
         Collections.sort(resultsList);
-        html.append("<h2>Passed [" + resultsList.size() + "]</h2>");
-        for (String result : resultsList) {
+        html.append("<h2>" + title + " [" + resultsList.size() + "]</h2>");
+        for (AbstractResult result : resultsList) {
             html.append("<h3>");
-            html.append("<a href=\"" + getViewSourceUrl(result).toString() + "\"");
+            html.append("<a href=\"" + getViewSourceUrl(result.getRelativePath()).toString() +
+                    "\"");
             html.append(" target=\"_blank\">");
             html.append("<span class=\"sqr\">&#x25a0; </span>");
-            html.append("<span class=\"path\">" + result + "</span>");
+            html.append("<span class=\"path\">" + result.getRelativePath() + "</span>");
             html.append("</a>");
             html.append("</h3>");
+            appendExpectedResultsSources(result, html);
             html.append("<div class=\"space\"></div>");
         }
     }
 
+    private static final void appendExpectedResultsSources(AbstractResult result,
+            StringBuilder html) {
+        String textSource = result.getExpectedTextResultPath();
+        String imageSource = result.getExpectedImageResultPath();
+
+        if (textSource != null) {
+            html.append("<span class=\"source\">Expected textual result from: ");
+            html.append("<a href=\"" + ForwarderManager.getHostSchemePort(false) + "LayoutTests/" +
+                    textSource + "\"");
+            html.append(" target=\"_blank\">");
+            html.append(textSource + "</a></span>");
+        }
+        if (imageSource != null) {
+            html.append("<span class=\"source\">Expected image result from: ");
+            html.append("<a href=\"" + ForwarderManager.getHostSchemePort(false) + "LayoutTests/" +
+                    imageSource + "\"");
+            html.append(" target=\"_blank\">");
+            html.append(imageSource + "</a></span>");
+        }
+    }
+
     private static final URL getViewSourceUrl(String relativePath) {
         URL url = null;
         try {
@@ -411,4 +481,4 @@
         }
         return url;
     }
-}
\ No newline at end of file
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
index 4965fd9..9db4d2b 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
@@ -19,10 +19,14 @@
 import android.app.Activity;
 import android.app.ProgressDialog;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.view.Gravity;
 import android.view.Window;
+import android.webkit.WebView;
+import android.widget.Toast;
 
 import com.android.dumprendertree2.scriptsupport.OnEverythingFinishedCallback;
 
@@ -124,13 +128,38 @@
     }
 
     private void onEverythingFinishedIntent(Intent intent) {
-        /** TODO: Show some kind of summary to the user */
+        Toast toast = Toast.makeText(this,
+                "All tests finished.\nPress back key to return to the tests' list.",
+                Toast.LENGTH_LONG);
+        toast.setGravity(Gravity.CENTER, -40, 0);
+        toast.show();
+
+        /** Show the details to the user */
+        WebView webView = new WebView(this);
+        webView.getSettings().setJavaScriptEnabled(true);
+        webView.getSettings().setBuiltInZoomControls(true);
+        webView.getSettings().setEnableSmoothTransition(true);
+        /** This enables double-tap to zoom */
+        webView.getSettings().setUseWideViewPort(true);
+
+        setContentView(webView);
+        webView.loadUrl(Summarizer.getDetailsUri().toString());
+
         mEverythingFinished = true;
         if (mOnEverythingFinishedCallback != null) {
             mOnEverythingFinishedCallback.onFinished();
         }
     }
 
+    /**
+     * This, together with android:configChanges="orientation" in manifest file, prevents
+     * the activity from restarting on orientation change.
+     */
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+    }
+
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         outState.putStringArrayList("testsList", mTestsList);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
index 9fa2335..c714ec4 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
@@ -99,11 +99,14 @@
                     false, false)) {
                 itemName = new File(testRelativePath).getName();
                 if (FileFilter.isTestFile(itemName)) {
-                    if (!mFileFilter.isSkip(testRelativePath)) {
+                    /** We chose to skip all the tests that are expected to crash. */
+                    if (!mFileFilter.isCrash(testRelativePath)) {
                         mTestsList.add(testRelativePath);
                     } else {
-                        //mSummarizer.addSkippedTest(relativePath);
-                        /** TODO: Summarizer is now in service - figure out how to send the info */
+                        /**
+                         * TODO: Summarizer is now in service - figure out how to send the info.
+                         * Previously: mSummarizer.addSkippedTest(relativePath);
+                         */
                     }
                 }
             }
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
index 0f864e5..9664efe 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
@@ -36,6 +36,7 @@
     private static final int MSG_DOCUMENT_AS_TEXT = 0;
 
     private String mExpectedResult;
+    private String mExpectedResultPath;
     private String mActualResult;
     private String mRelativePath;
     private ResultCode mResultCode;
@@ -69,6 +70,7 @@
      */
     public TextResult(Bundle bundle) {
         mExpectedResult = bundle.getString("expectedTextualResult");
+        mExpectedResultPath = bundle.getString("expectedTextualResultPath");
         mActualResult = bundle.getString("actualTextualResult");
         setAdditionalTextOutputString(bundle.getString("additionalTextOutputString"));
         mRelativePath = bundle.getString("relativePath");
@@ -116,6 +118,27 @@
     }
 
     @Override
+    public void setExpectedImageResultPath(String relativePath) {
+        /** This method is not applicable to this type of result */
+    }
+
+    @Override
+    public String getExpectedImageResultPath() {
+        /** This method is not applicable to this type of result */
+        return null;
+    }
+
+    @Override
+    public void setExpectedTextResultPath(String relativePath) {
+        mExpectedResultPath = relativePath;
+    }
+
+    @Override
+    public String getExpectedTextResultPath() {
+        return mExpectedResultPath;
+    }
+
+    @Override
     public void setExpectedTextResult(String expectedResult) {
         mExpectedResult = expectedResult;
     }
@@ -212,6 +235,7 @@
     public Bundle getBundle() {
         Bundle bundle = new Bundle();
         bundle.putString("expectedTextualResult", mExpectedResult);
+        bundle.putString("expectedTextualResultPath", mExpectedResultPath);
         bundle.putString("actualTextualResult", getActualTextResult());
         bundle.putString("additionalTextOutputString", getAdditionalTextOutputString());
         bundle.putString("relativePath", mRelativePath);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
index 2ffb48f..5e9f24e 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
@@ -32,6 +32,10 @@
 
     private static final String LOG_TAG = "ConnectionHandler";
 
+    public static interface OnFinishedCallback {
+        public void onFinished();
+    }
+
     private class SocketPipeThread extends Thread {
 
         private Socket mInSocket, mOutSocket;
@@ -52,6 +56,7 @@
                 }
             } catch (IOException e) {
                 Log.w(LOG_TAG, this.toString(), e);
+                finish();
                 return;
             }
 
@@ -69,6 +74,18 @@
                     break;
                 }
             }
+
+            finish();
+        }
+
+        private void finish() {
+            synchronized (mThreadsRunning) {
+                mThreadsRunning--;
+                if (mThreadsRunning == 0) {
+                    ConnectionHandler.this.stop();
+                    mOnFinishedCallback.onFinished();
+                }
+            }
         }
 
         @Override
@@ -77,9 +94,13 @@
         }
     }
 
+    private Integer mThreadsRunning;
+
     private Socket mFromSocket, mToSocket;
     private SocketPipeThread mFromToPipe, mToFromPipe;
 
+    private OnFinishedCallback mOnFinishedCallback;
+
     public ConnectionHandler(Socket fromSocket, Socket toSocket) {
         mFromSocket = fromSocket;
         mToSocket = toSocket;
@@ -87,7 +108,13 @@
         mToFromPipe = new SocketPipeThread(mToSocket, mFromSocket);
     }
 
+    public void registerOnConnectionHandlerFinishedCallback(OnFinishedCallback callback) {
+        mOnFinishedCallback = callback;
+    }
+
     public void start() {
+        /** We have 2 threads running, one for each pipe, that we start here. */
+        mThreadsRunning = 2;
         mFromToPipe.start();
         mToFromPipe.start();
     }
@@ -102,17 +129,23 @@
             synchronized (mToFromPipe) {
                 /** This will stop the while loop in the run method */
                 try {
-                    socket.shutdownInput();
+                    if (!socket.isInputShutdown()) {
+                        socket.shutdownInput();
+                    }
                 } catch (IOException e) {
                     Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
                 }
                 try {
-                    socket.shutdownOutput();
+                    if (!socket.isOutputShutdown()) {
+                        socket.shutdownOutput();
+                    }
                 } catch (IOException e) {
                     Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
                 }
                 try {
-                    socket.close();
+                    if (!socket.isClosed()) {
+                        socket.close();
+                    }
                 } catch (IOException e) {
                     Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
                 }
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
index f948767..31cd8ea 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
@@ -34,7 +34,6 @@
     private int mPort;
     private String mRemoteMachineIpAddress;
 
-    private Boolean mIsRunning = false;
     private ServerSocket mServerSocket;
 
     private Set<ConnectionHandler> mConnectionHandlers = new HashSet<ConnectionHandler>();
@@ -55,47 +54,69 @@
             return;
         }
 
-        mIsRunning = true;
         super.start();
     }
 
     @Override
     public void run() {
         while (true) {
-            synchronized (this) {
-                if (!mIsRunning) {
-                    return;
-                }
+            /** These sockets will be closed when Forwarder.stop() is called */
+            Socket localSocket;
+            Socket remoteSocket;
+            try {
+                localSocket = mServerSocket.accept();
+                remoteSocket = AdbUtils.getSocketToRemoteMachine(mRemoteMachineIpAddress,
+                        mPort);
+            } catch (IOException e) {
+                /** This most likely means that mServerSocket is already closed */
+                Log.w(LOG_TAG, "mPort=" + mPort, e);
+                break;
+            }
 
-                /** These sockets will be closed when Forwarder.stop() is called */
-                Socket localSocket;
-                Socket remoteSocket;
+            if (remoteSocket == null) {
                 try {
-                    localSocket = mServerSocket.accept();
-                    remoteSocket = AdbUtils.getSocketToRemoteMachine(mRemoteMachineIpAddress,
-                            mPort);
+                    localSocket.close();
                 } catch (IOException e) {
-                    /** This most likely means that mServerSocket is already closed */
-                    Log.w(LOG_TAG, "mPort=" + mPort, e);
-                    return;
+                    Log.e(LOG_TAG, "mPort=" + mPort, e);
                 }
 
-                if (remoteSocket == null) {
-                    try {
-                        localSocket.close();
-                    } catch (IOException e) {
-                        Log.e(LOG_TAG, "mPort=" + mPort, e);
+                Log.e(LOG_TAG, "run(): mPort= " + mPort + " Failed to start forwarding from " +
+                        localSocket);
+                continue;
+            }
+
+            final ConnectionHandler connectionHandler =
+                    new ConnectionHandler(localSocket, remoteSocket);
+
+            /**
+             * We have to close the sockets after the ConnectionHandler finishes, so we
+             * don't get "Too may open files" exception. We also remove the ConnectionHandler
+             * from the collection to avoid memory issues.
+             * */
+            ConnectionHandler.OnFinishedCallback callback =
+                    new ConnectionHandler.OnFinishedCallback() {
+                @Override
+                public void onFinished() {
+                    synchronized (this) {
+                        if (mConnectionHandlers.remove(connectionHandler)) {
+                            Log.d(LOG_TAG, "removeConnectionHandler(): removed");
+                        } else {
+                            assert false : "removeConnectionHandler(): not in the collection";
+                        }
                     }
-
-                    Log.e(LOG_TAG, "run(): mPort= " + mPort + " Failed to start forwarding from " +
-                            localSocket);
-                    continue;
                 }
+            };
+            connectionHandler.registerOnConnectionHandlerFinishedCallback(callback);
 
-                ConnectionHandler forwarder = new ConnectionHandler(localSocket, remoteSocket);
-                mConnectionHandlers.add(forwarder);
-                forwarder.start();
+            synchronized (this) {
+                mConnectionHandlers.add(connectionHandler);
+            }
+            connectionHandler.start();
+        }
 
+        synchronized (this) {
+            for (ConnectionHandler connectionHandler : mConnectionHandlers) {
+                connectionHandler.stop();
             }
         }
     }
@@ -106,14 +127,5 @@
         } catch (IOException e) {
             Log.e(LOG_TAG, "mPort=" + mPort, e);
         }
-
-        synchronized (this) {
-            mIsRunning = false;
-        }
-
-        for (ConnectionHandler connectionHandler : mConnectionHandlers) {
-            connectionHandler.stop();
-        }
-        mConnectionHandlers.clear();
     }
 }
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
index 2280c66..b1862ef 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
@@ -35,6 +35,9 @@
 import android.os.Handler;
 import android.os.Message;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
@@ -205,11 +208,7 @@
                     showDir(item.getRelativePath());
                 } else {
                     /** Run the test */
-                    Intent intent = new Intent();
-                    intent.setClass(DirListActivity.this, TestsListActivity.class);
-                    intent.setAction(Intent.ACTION_RUN);
-                    intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, item.getRelativePath());
-                    startActivity(intent);
+                    runAllTestsUnder(item.getRelativePath());
                 }
             }
         });
@@ -236,6 +235,32 @@
         showDir("");
     }
 
+    private void runAllTestsUnder(String relativePath) {
+        Intent intent = new Intent();
+        intent.setClass(DirListActivity.this, TestsListActivity.class);
+        intent.setAction(Intent.ACTION_RUN);
+        intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, relativePath);
+        startActivity(intent);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.gui_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.run_all:
+                runAllTestsUnder(mCurrentDirPath);
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
     @Override
     /**
      * Moves to the parent directory if one exists. Does not allow to move above
@@ -278,13 +303,7 @@
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
                         removeDialog(DIALOG_RUN_ABORT_DIR);
-                        /** Run the tests */
-                        Intent intent = new Intent();
-                        intent.setClass(DirListActivity.this, TestsListActivity.class);
-                        intent.setAction(Intent.ACTION_RUN);
-                        intent.putExtra(TestsListActivity.EXTRA_TEST_PATH,
-                                args.getString("relativePath"));
-                        startActivity(intent);
+                        runAllTestsUnder(args.getString("relativePath"));
                     }
                 });
 
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 34cbc44..db1e5ef 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -31,6 +31,16 @@
         </activity>
         
         <activity
+                android:name="ResizeActivity"
+                android:label="_Resize"
+                android:windowSoftInputMode="adjustResize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
                 android:name="TextGammaActivity"
                 android:label="_Gamma"
                 android:theme="@android:style/Theme.Translucent.NoTitleBar">
diff --git a/tests/HwAccelerationTest/res/layout/form.xml b/tests/HwAccelerationTest/res/layout/form.xml
new file mode 100644
index 0000000..0b17db1
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/form.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="First name" />
+        
+        <EditText
+            android:layout_width="0dip"
+            android:layout_weight="1.0"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="3dip"
+            android:hint="Enter your first name" />
+        
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Last name" />
+        
+        <EditText
+            android:layout_width="0dip"
+            android:layout_weight="1.0"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="3dip"
+            android:hint="Enter your last name" />
+        
+    </LinearLayout>
+    
+    <EditText
+        android:layout_width="match_parent"
+        android:layout_weight="1.0"
+        android:layout_height="0dip"
+        android:background="#ff7f0000" />
+    
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ResizeActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ResizeActivity.java
new file mode 100644
index 0000000..e5771b8
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ResizeActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ResizeActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        setContentView(R.layout.form);
+    }
+}
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index b50a393..eaff0f4 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -856,6 +856,15 @@
                                     error.string());
                             goto bail;
                         }
+                    } else if (tag == "uses-package") {
+                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        if (name != "" && error == "") {
+                            printf("uses-package:'%s'\n", name.string());
+                        } else {
+                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
+                                    error.string());
+                                goto bail;
+                        }
                     } else if (tag == "original-package") {
                         String8 name = getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
diff --git a/voip/java/android/net/sip/ISipSession.aidl b/voip/java/android/net/sip/ISipSession.aidl
index fbcb056..1a23527 100644
--- a/voip/java/android/net/sip/ISipSession.aidl
+++ b/voip/java/android/net/sip/ISipSession.aidl
@@ -115,8 +115,7 @@
      * @param sessionDescription the session description of this call
      * @see ISipSessionListener
      */
-    void makeCall(in SipProfile callee,
-            in SessionDescription sessionDescription);
+    void makeCall(in SipProfile callee, String sessionDescription);
 
     /**
      * Answers an incoming call with the specified session description. The
@@ -125,7 +124,7 @@
      *
      * @param sessionDescription the session description to answer this call
      */
-    void answerCall(in SessionDescription sessionDescription);
+    void answerCall(String sessionDescription);
 
     /**
      * Ends an established call, terminates an outgoing call or rejects an
@@ -143,5 +142,5 @@
      *
      * @param sessionDescription the new session description
      */
-    void changeCall(in SessionDescription sessionDescription);
+    void changeCall(String sessionDescription);
 }
diff --git a/voip/java/android/net/sip/ISipSessionListener.aidl b/voip/java/android/net/sip/ISipSessionListener.aidl
index 8570958..c552a57 100644
--- a/voip/java/android/net/sip/ISipSessionListener.aidl
+++ b/voip/java/android/net/sip/ISipSessionListener.aidl
@@ -39,7 +39,7 @@
      * @param sessionDescription the caller's session description
      */
     void onRinging(in ISipSession session, in SipProfile caller,
-            in byte[] sessionDescription);
+            String sessionDescription);
 
     /**
      * Called when a RINGING response is received for the INVITE request sent
@@ -55,7 +55,7 @@
      * @param sessionDescription the peer's session description
      */
     void onCallEstablished(in ISipSession session,
-            in byte[] sessionDescription);
+            String sessionDescription);
 
     /**
      * Called when the session is terminated.
diff --git a/voip/java/android/net/sip/SdpSessionDescription.java b/voip/java/android/net/sip/SdpSessionDescription.java
index 0c29935..f6ae837 100644
--- a/voip/java/android/net/sip/SdpSessionDescription.java
+++ b/voip/java/android/net/sip/SdpSessionDescription.java
@@ -186,8 +186,8 @@
             }
         }
 
-        public SdpSessionDescription build() {
-            return mSdp;
+        public String build() {
+            return mSdp.toString();
         }
     }
 
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 3cdd114..8254543 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -168,9 +168,9 @@
      * Attaches an incoming call to this call object.
      *
      * @param session the session that receives the incoming call
-     * @param sdp the session description of the incoming call
+     * @param sessionDescription the session description of the incoming call
      */
-    void attachCall(ISipSession session, SdpSessionDescription sdp)
+    void attachCall(ISipSession session, String sessionDescription)
             throws SipException;
 
     /** Ends a call. */
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index b8ac6d7..bd2c1cd 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -28,14 +28,6 @@
 import android.net.rtp.AudioGroup;
 import android.net.rtp.AudioStream;
 import android.net.rtp.RtpStream;
-import android.net.sip.ISipSession;
-import android.net.sip.SdpSessionDescription;
-import android.net.sip.SessionDescription;
-import android.net.sip.SipAudioCall;
-import android.net.sip.SipManager;
-import android.net.sip.SipProfile;
-import android.net.sip.SipSessionAdapter;
-import android.net.sip.SipSessionState;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.Vibrator;
@@ -196,7 +188,7 @@
 
     @Override
     public synchronized void onRinging(ISipSession session,
-            SipProfile peerProfile, byte[] sessionDescription) {
+            SipProfile peerProfile, String sessionDescription) {
         try {
             if ((mSipSession == null) || !mInCall
                     || !session.getCallId().equals(mSipSession.getCallId())) {
@@ -218,7 +210,7 @@
         }
     }
 
-    private synchronized void establishCall(byte[] sessionDescription) {
+    private synchronized void establishCall(String sessionDescription) {
         stopRingbackTone();
         stopRinging();
         try {
@@ -234,7 +226,7 @@
 
     @Override
     public void onCallEstablished(ISipSession session,
-            byte[] sessionDescription) {
+            String sessionDescription) {
         establishCall(sessionDescription);
         Listener listener = mListener;
         if (listener != null) {
@@ -312,10 +304,10 @@
     }
 
     public synchronized void attachCall(ISipSession session,
-            SdpSessionDescription sdp) throws SipException {
+            String sessionDescription) throws SipException {
         mSipSession = session;
-        mPeerSd = sdp;
         try {
+            mPeerSd = new SdpSessionDescription(sessionDescription);
             session.setListener(this);
         } catch (Throwable e) {
             Log.e(TAG, "attachCall()", e);
@@ -390,12 +382,12 @@
         if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL);
     }
 
-    private SessionDescription createOfferSessionDescription() {
+    private String createOfferSessionDescription() {
         AudioCodec[] codecs = AudioCodec.getSystemSupportedCodecs();
         return createSdpBuilder(true, convert(codecs)).build();
     }
 
-    private SessionDescription createAnswerSessionDescription() {
+    private String createAnswerSessionDescription() {
         try {
             // choose an acceptable media from mPeerSd to answer
             SdpSessionDescription.AudioCodec codec = getCodec(mPeerSd);
@@ -412,7 +404,7 @@
         }
     }
 
-    private SessionDescription createHoldSessionDescription() {
+    private String createHoldSessionDescription() {
         try {
             return createSdpBuilder(false, mCodec)
                     .addMediaAttribute(AUDIO, "sendonly", (String) null)
@@ -422,7 +414,7 @@
         }
     }
 
-    private SessionDescription createContinueSessionDescription() {
+    private String createContinueSessionDescription() {
         return createSdpBuilder(true, mCodec).build();
     }
 
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 287a13a..beec8fe 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -69,22 +70,42 @@
     private ISipService mSipService;
 
     /**
-     * Creates a manager instance and initializes the background SIP service.
-     * Will be removed once the SIP service is integrated into framework.
+     * Gets a manager instance. Returns null if SIP API is not supported.
      *
-     * @param context context to start the SIP service
-     * @return the manager instance
+     * @param context application context for checking if SIP API is supported
+     * @return the manager instance or null if SIP API is not supported
      */
-    public static SipManager getInstance(final Context context) {
-        final SipManager manager = new SipManager();
-        manager.createSipService(context);
-        return manager;
+    public static SipManager getInstance(Context context) {
+        return (isApiSupported(context) ? new SipManager() : null);
+    }
+
+    /**
+     * Returns true if the SIP API is supported by the system.
+     */
+    public static boolean isApiSupported(Context context) {
+        return true;
+        /* 
+        return context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_SIP);
+         */
+    }
+
+    /**
+     * Returns true if the system supports SIP-based VoIP.
+     */
+    public static boolean isVoipSupported(Context context) {
+        return true;
+        /* 
+        return context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context);
+         */
     }
 
     private SipManager() {
+        createSipService();
     }
 
-    private void createSipService(Context context) {
+    private void createSipService() {
         if (mSipService != null) return;
         IBinder b = ServiceManager.getService(Context.SIP_SERVICE);
         mSipService = ISipService.Stub.asInterface(b);
@@ -283,21 +304,19 @@
             throw new SipException("Call ID missing in incoming call intent");
         }
 
-        byte[] offerSd = getOfferSessionDescription(incomingCallIntent);
+        String offerSd = getOfferSessionDescription(incomingCallIntent);
         if (offerSd == null) {
             throw new SipException("Session description missing in incoming "
                     + "call intent");
         }
 
         try {
-            SdpSessionDescription sdp = new SdpSessionDescription(offerSd);
-
             ISipSession session = mSipService.getPendingSession(callId);
             if (session == null) return null;
             SipAudioCall call = new SipAudioCallImpl(
                     context, session.getLocalProfile());
             call.setRingtoneEnabled(ringtoneEnabled);
-            call.attachCall(session, sdp);
+            call.attachCall(session, offerSd);
             call.setListener(listener);
             return call;
         } catch (Throwable t) {
@@ -314,7 +333,7 @@
     public static boolean isIncomingCallIntent(Intent intent) {
         if (intent == null) return false;
         String callId = getCallId(intent);
-        byte[] offerSd = getOfferSessionDescription(intent);
+        String offerSd = getOfferSessionDescription(intent);
         return ((callId != null) && (offerSd != null));
     }
 
@@ -336,8 +355,8 @@
      * @return the offer session description or null if the intent does not
      *      have it
      */
-    public static byte[] getOfferSessionDescription(Intent incomingCallIntent) {
-        return incomingCallIntent.getByteArrayExtra(OFFER_SD_KEY);
+    public static String getOfferSessionDescription(Intent incomingCallIntent) {
+        return incomingCallIntent.getStringExtra(OFFER_SD_KEY);
     }
 
     /**
@@ -350,7 +369,7 @@
      * @hide
      */
     public static Intent createIncomingCallBroadcast(String action,
-            String callId, byte[] sessionDescription) {
+            String callId, String sessionDescription) {
         Intent intent = new Intent(action);
         intent.putExtra(CALL_ID_KEY, callId);
         intent.putExtra(OFFER_SD_KEY, sessionDescription);
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index 6c99141..aa2da75 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -46,8 +46,7 @@
     private String mProfileName;
     private boolean mSendKeepAlive = false;
     private boolean mAutoRegistration = true;
-    private boolean mAllowOutgoingCall = false;
-    private int mCallingUid = -1;
+    private transient int mCallingUid = 0;
 
     /** @hide */
     public static final Parcelable.Creator<SipProfile> CREATOR =
@@ -245,18 +244,6 @@
         }
 
         /**
-         * Sets the allow-outgoing-call flag.
-         *
-         * @param flag true if allowing to make outgoing call on the profile;
-         *      false otherwise
-         * @return this builder object
-         */
-        public Builder setOutgoingCallAllowed(boolean flag) {
-            mProfile.mAllowOutgoingCall = flag;
-            return this;
-        }
-
-        /**
          * Builds and returns the SIP profile object.
          *
          * @return the profile object created
@@ -293,7 +280,6 @@
         mProfileName = in.readString();
         mSendKeepAlive = (in.readInt() == 0) ? false : true;
         mAutoRegistration = (in.readInt() == 0) ? false : true;
-        mAllowOutgoingCall = (in.readInt() == 0) ? false : true;
         mCallingUid = in.readInt();
     }
 
@@ -307,7 +293,6 @@
         out.writeString(mProfileName);
         out.writeInt(mSendKeepAlive ? 1 : 0);
         out.writeInt(mAutoRegistration ? 1 : 0);
-        out.writeInt(mAllowOutgoingCall ? 1 : 0);
         out.writeInt(mCallingUid);
     }
 
@@ -435,13 +420,6 @@
     }
 
     /**
-     * Returns true if allowing to make outgoing calls on this profile.
-     */
-    public boolean isOutgoingCallAllowed() {
-        return mAllowOutgoingCall;
-    }
-
-    /**
      * Sets the calling process's Uid in the sip service.
      * @hide
      */
diff --git a/voip/java/android/net/sip/SipSessionAdapter.java b/voip/java/android/net/sip/SipSessionAdapter.java
index cfb71d7..770d4eb 100644
--- a/voip/java/android/net/sip/SipSessionAdapter.java
+++ b/voip/java/android/net/sip/SipSessionAdapter.java
@@ -26,14 +26,14 @@
     }
 
     public void onRinging(ISipSession session, SipProfile caller,
-            byte[] sessionDescription) {
+            String sessionDescription) {
     }
 
     public void onRingingBack(ISipSession session) {
     }
 
-    public void onCallEstablished(
-            ISipSession session, byte[] sessionDescription) {
+    public void onCallEstablished(ISipSession session,
+            String sessionDescription) {
     }
 
     public void onCallEnded(ISipSession session) {
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 47f8813..862a61b 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -111,6 +111,10 @@
 
     public native static boolean setPowerModeCommand(int mode);
 
+    public native static int getBandCommand();
+
+    public native static boolean setBandCommand(int band);
+
     public native static int getPowerModeCommand();
 
     public native static boolean setNumAllowedChannelsCommand(int numChannels);
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 6016526..e1cbf5b 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -320,6 +320,13 @@
     private static final int SCAN_ACTIVE = 1;
     private static final int SCAN_PASSIVE = 2;
 
+    /* Auto allows 802.11A/B/G operation */
+    private static final int BAND_AUTO = 0;
+    /* 5GHz allows 802.11A operation */
+    private static final int BAND_5G = 1;
+    /* 2.4GHz allows 802.11B/G operation */
+    private static final int BAND_2G = 2;
+
     /**
      * The maximum number of times we will retry a connection to an access point
      * for which we have failed in acquiring an IP address from DHCP. A value of
@@ -2179,6 +2186,14 @@
 
             /* Initialize channel count */
             setNumAllowedChannels();
+            /*
+             * STOPSHIP
+             * TODO: We are having 11A issues that Broadcom is looking
+             * to resolve, this is a temporary fix to allow only 11B/G
+             * and help improve GoogleGuest connectivity
+             * We also need to add the UI for band control
+             */
+            WifiNative.setBandCommand(BAND_2G);
 
             if (mIsScanMode) {
                 WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);