Merge "Remove unused intent from filter and unused method."
diff --git a/api/current.xml b/api/current.xml
index 813f5dc..9e86f01 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -8897,6 +8897,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"
@@ -19840,8 +19851,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<exception name="CloneNotSupportedException" type="java.lang.CloneNotSupportedException">
-</exception>
 </method>
 <method name="end"
  return="void"
@@ -19854,6 +19863,17 @@
  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;"
  abstract="false"
@@ -19865,6 +19885,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"
@@ -19900,6 +19931,80 @@
 <parameter name="listener" type="android.animation.Animatable.AnimatableListener">
 </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"
@@ -20219,6 +20324,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"
@@ -20555,6 +20671,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="java.lang.Cloneable">
+</implements>
 <constructor name="Keyframe"
  type="android.animation.Keyframe"
  static="false"
@@ -20576,6 +20694,42 @@
 >
 <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>
@@ -20603,6 +20757,17 @@
 <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"
@@ -20687,6 +20852,333 @@
 </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="getAnimatable"
+ return="android.animation.Animatable"
+ 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="setAnimatable"
+ 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="animatable" type="android.animation.Animatable">
+</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="PropertyAnimator"
  extends="android.animation.Animator"
  abstract="false"
@@ -20768,19 +21260,6 @@
 <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"
@@ -20790,6 +21269,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="java.lang.Cloneable">
+</implements>
 <constructor name="PropertyValuesHolder"
  type="android.animation.PropertyValuesHolder"
  static="false"
@@ -20812,6 +21293,17 @@
 <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"
@@ -20974,6 +21466,28 @@
  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"
@@ -21024,7 +21538,7 @@
 <parameter name="sequenceItems" type="android.animation.Animatable...">
 </parameter>
 </method>
-<method name="setTarget"
+<method name="setDuration"
  return="void"
  abstract="false"
  native="false"
@@ -21034,7 +21548,33 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="target" type="java.lang.Object">
+<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>
@@ -55586,6 +56126,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"
@@ -55597,6 +56148,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"
@@ -139564,6 +140126,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"
@@ -146739,6 +147316,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"
@@ -146849,7 +147430,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"
@@ -148634,7 +149215,7 @@
  value="1"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -192776,6 +193357,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"
@@ -192820,6 +193423,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"
@@ -194399,6 +195024,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"
@@ -195488,6 +196126,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"
@@ -197092,6 +197741,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"
@@ -197360,6 +198022,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"
@@ -197568,6 +198243,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"
@@ -197810,6 +198498,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"
@@ -197994,6 +198695,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"
@@ -199056,6 +199770,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"
@@ -200884,6 +201635,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"
@@ -232322,6 +233086,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>
@@ -232339,6 +233115,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"
diff --git a/core/java/android/animation/Animatable.java b/core/java/android/animation/Animatable.java
index d6cf7c0..3fdf200 100644
--- a/core/java/android/animation/Animatable.java
+++ b/core/java/android/animation/Animatable.java
@@ -16,6 +16,8 @@
 
 package android.animation;
 
+import android.view.animation.Interpolator;
+
 import java.util.ArrayList;
 
 /**
@@ -56,6 +58,46 @@
     public void end() {
     }
 
+    /**
+     * 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 abstract long getStartDelay();
+
+    /**
+     * 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 abstract void setStartDelay(long startDelay);
+
+
+    /**
+     * Sets the length of the animation.
+     *
+     * @param duration The length of the animation, in milliseconds.
+     */
+    public abstract void setDuration(long duration);
+
+    /**
+     * Gets the length of the animation.
+     *
+     * @return The length of the animation, in milliseconds.
+     */
+    public abstract long getDuration();
+
+    /**
+     * 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
+     */
+    public abstract void setInterpolator(Interpolator value);
 
     /**
      * Returns whether this Animatable is currently running (having been started and not yet ended).
@@ -115,17 +157,56 @@
     }
 
     @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));
+    public Animatable clone() {
+        try {
+            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;
+        } catch (CloneNotSupportedException e) {
+           throw new AssertionError();
         }
-        return anim;
+    }
+
+    /**
+     * This method tells the object to use appropriate information to extract
+     * starting values for the animation. For example, a Sequencer object will pass
+     * this call to its child objects to tell them to set up the values. A
+     * PropertyAnimator object will use the information it has about its target object
+     * and PropertyValuesHolder objects to get the start values for its properties.
+     * An Animator object will ignore the request since it does not have enough
+     * information (such as a target object) to gather these values.
+     */
+    public void setupStartValues() {
+    }
+
+    /**
+     * This method tells the object to use appropriate information to extract
+     * ending values for the animation. For example, a Sequencer object will pass
+     * this call to its child objects to tell them to set up the values. A
+     * PropertyAnimator object will use the information it has about its target object
+     * and PropertyValuesHolder objects to get the start values for its properties.
+     * An Animator object will ignore the request since it does not have enough
+     * information (such as a target object) to gather these values.
+     */
+    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 android.animation.Animator}, 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) {
     }
 
     /**
diff --git a/core/java/android/animation/AnimatableListenerAdapter.java b/core/java/android/animation/AnimatableListenerAdapter.java
index 25a842b..c169b28 100644
--- a/core/java/android/animation/AnimatableListenerAdapter.java
+++ b/core/java/android/animation/AnimatableListenerAdapter.java
@@ -26,28 +26,28 @@
 public abstract class AnimatableListenerAdapter implements AnimatableListener {
 
     /**
-     * @{inheritdoc}
+     * {@inheritdoc}
      */
     @Override
     public void onAnimationCancel(Animatable animation) {
     }
 
     /**
-     * @{inheritdoc}
+     * {@inheritdoc}
      */
     @Override
     public void onAnimationEnd(Animatable animation) {
     }
 
     /**
-     * @{inheritdoc}
+     * {@inheritdoc}
      */
     @Override
     public void onAnimationRepeat(Animatable animation) {
     }
 
     /**
-     * @{inheritdoc}
+     * {@inheritdoc}
      */
     @Override
     public void onAnimationStart(Animatable animation) {
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 8b74658..8e947ec 100755
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -145,10 +145,10 @@
     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
+     * Flag that denotes whether the animation is set up and ready to go. Used to
      * set up animation that has not yet been started.
      */
-    private boolean mInitialized = false;
+    boolean mInitialized = false;
 
     //
     // Backing variables
@@ -243,6 +243,14 @@
         }
     }
 
+    /**
+     * Sets the values, per property, being animated between. This function is called internally
+     * by the constructors of Animator that take a list of values. But an Animator 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;
@@ -254,6 +262,18 @@
     }
 
     /**
+     * Returns the values that this Animator animates between. These values are stored in
+     * PropertyValuesHolder objects, even if the Animator 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
@@ -286,12 +306,14 @@
      *  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();
+        if (!mInitialized) {
+            int numValues = mValues.length;
+            for (int i = 0; i < numValues; ++i) {
+                mValues[i].init();
+            }
+            mCurrentIteration = 0;
+            mInitialized = true;
         }
-        mCurrentIteration = 0;
-        mInitialized = true;
     }
 
 
@@ -324,9 +346,7 @@
      * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
      */
     public void setCurrentPlayTime(long playTime) {
-        if (!mInitialized) {
-            initAnimation();
-        }
+        initAnimation();
         long currentTime = AnimationUtils.currentAnimationTimeMillis();
         if (mPlayingState != RUNNING) {
             mSeekTime = playTime;
@@ -619,6 +639,7 @@
      *
      * @param value the interpolator to be used by this animation
      */
+    @Override
     public void setInterpolator(Interpolator value) {
         if (value != null) {
             mInterpolator = value;
@@ -783,6 +804,10 @@
      * 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;
@@ -898,7 +923,7 @@
     }
 
     @Override
-    public Animator clone() throws CloneNotSupportedException {
+    public Animator clone() {
         final Animator anim = (Animator) super.clone();
         if (mUpdateListeners != null) {
             ArrayList<AnimatorUpdateListener> oldListeners = mUpdateListeners;
@@ -919,7 +944,7 @@
             int numValues = oldValues.length;
             anim.mValues = new PropertyValuesHolder[numValues];
             for (int i = 0; i < numValues; ++i) {
-                anim.mValues[i] = oldValues[i];
+                anim.mValues[i] = oldValues[i].clone();
             }
             anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
             for (int i = 0; i < numValues; ++i) {
diff --git a/core/java/android/animation/Keyframe.java b/core/java/android/animation/Keyframe.java
index e2800b3..7d4d104 100644
--- a/core/java/android/animation/Keyframe.java
+++ b/core/java/android/animation/Keyframe.java
@@ -26,7 +26,7 @@
  * 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.
      */
@@ -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);
     }
 
     /**
@@ -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/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
new file mode 100644
index 0000000..5dfdfbd
--- /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#setAnimatable(int, Animatable)
+ * setAnimatable()} 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 PropertyAnimator 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 setAnimatable().
+     */
+    private Animatable mDisappearingAnim = null;
+    private Animatable mAppearingAnim = null;
+    private Animatable mChangingAppearingAnim = null;
+    private Animatable mChangingDisappearingAnim = null;
+
+    /**
+     * These are the default animations, defined in the constructor, that will be used
+     * unless the user specifies custom animations.
+     */
+    private static PropertyAnimator defaultChangeIn;
+    private static PropertyAnimator defaultChangeOut;
+    private static PropertyAnimator defaultFadeIn;
+    private static PropertyAnimator 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, Animatable> currentAnimations = new HashMap<View, Animatable>();
+
+    /**
+     * 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 PropertyAnimator<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 PropertyAnimator<Float>(DEFAULT_DURATION, this, "alpha", 0f, 1f);
+            defaultFadeIn.setStartDelay(mAppearingDelay);
+            defaultFadeIn.setInterpolator(mAppearingInterpolator);
+            defaultFadeOut =
+                    new PropertyAnimator<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 android.animation.Animatable#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 android.animation.Animatable#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 android.animation.Animatable#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 android.animation.Animatable#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 android.animation.Animatable#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 android.animation.Animatable#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
+     * Animatable object can be used, but to be most useful in the context of layout
+     * transitions, the animation should either be a PropertyAnimator or a Sequencer
+     * of animations including PropertyAnimators. Also, these PropertyAnimator objects
+     * should be able to get and set values on their target objects automatically. For
+     * example, a PropertyAnimator 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 PropertyAnimator 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 animatable The animation being assigned.
+     */
+    public void setAnimatable(int transitionType, Animatable animatable) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingAnim = (animatable != null) ? animatable : defaultChangeIn;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingAnim = (animatable != null) ? animatable : defaultChangeOut;
+                break;
+            case APPEARING:
+                mAppearingAnim = (animatable != null) ? animatable : defaultFadeIn;
+                break;
+            case DISAPPEARING:
+                mDisappearingAnim = (animatable != null) ? animatable : 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 Animatable The animation being used for the given transition type.
+     * @see #setAnimatable(int, Animatable)
+     */
+    public Animatable getAnimatable(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
+                Animatable currentAnimation = currentAnimations.get(child);
+                if (currentAnimation != null) {
+                    currentAnimation.cancel();
+                    currentAnimations.remove(child);
+                }
+
+                // Make a copy of the appropriate animation
+                final Animatable anim = (changeReason == APPEARING) ?
+                        mChangingAppearingAnim.clone() :
+                        mChangingDisappearingAnim.clone();
+
+                // Set the target object for the animation
+                anim.setTarget(child);
+
+                // A PropertyAnimator (or Sequencer 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 AnimatableListenerAdapter() {
+                            private boolean canceled = false;
+                            public void onAnimationCancel(Animatable animatable) {
+                                // we remove canceled animations immediately, not here
+                                canceled = true;
+                            }
+                            public void onAnimationEnd(Animatable animatable) {
+                                if (!canceled) {
+                                    currentAnimations.remove(child);
+                                }
+                            }
+                        });
+                        if (anim instanceof PropertyAnimator) {
+                            ((PropertyAnimator) 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) {
+        Animatable anim = mAppearingAnim.clone();
+        anim.setTarget(child);
+        anim.setStartDelay(mAppearingDelay);
+        anim.setDuration(mAppearingDuration);
+        if (anim instanceof PropertyAnimator) {
+            ((PropertyAnimator) anim).setCurrentPlayTime(0);
+        }
+        if (mListeners != null) {
+            anim.addListener(new AnimatableListenerAdapter() {
+                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) {
+        Animatable anim = mDisappearingAnim.clone();
+        anim.setStartDelay(mDisappearingDelay);
+        anim.setDuration(mDisappearingDuration);
+        anim.setTarget(child);
+        if (mListeners != null) {
+            anim.addListener(new AnimatableListenerAdapter() {
+                public void onAnimationEnd() {
+                    for (TransitionListener listener : mListeners) {
+                        listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
+                    }
+                }
+            });
+        }
+        if (anim instanceof PropertyAnimator) {
+            ((PropertyAnimator) 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/PropertyAnimator.java
index 8a6edcc..e555cc6 100644
--- a/core/java/android/animation/PropertyAnimator.java
+++ b/core/java/android/animation/PropertyAnimator.java
@@ -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,7 +239,7 @@
     }
 
     @Override
-    public PropertyAnimator clone() throws CloneNotSupportedException {
+    public PropertyAnimator clone() {
         final PropertyAnimator anim = (PropertyAnimator) super.clone();
         return anim;
     }
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index fc829b8..b6ff54e 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -25,8 +25,12 @@
 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 Animator or PropertyAnimator 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,
@@ -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
@@ -356,6 +362,63 @@
     }
 
     /**
+     * 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 PropertyAnimator 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 PropertyAnimator 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
@@ -381,8 +444,9 @@
      */
     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;
         }
     }
 
diff --git a/core/java/android/animation/Sequencer.java b/core/java/android/animation/Sequencer.java
index 8779b3d..04bede0 100644
--- a/core/java/android/animation/Sequencer.java
+++ b/core/java/android/animation/Sequencer.java
@@ -20,6 +20,7 @@
 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;
@@ -96,6 +97,16 @@
      */
     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 Sequencer. 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.
      *
@@ -153,6 +164,7 @@
      *
      * @param target The object being animated
      */
+    @Override
     public void setTarget(Object target) {
         for (Node node : mNodes) {
             Animatable animation = node.animation;
@@ -165,6 +177,19 @@
     }
 
     /**
+     * Sets the Interpolator for all current {@link #getChildAnimations() child animations}
+     * of this Sequencer.
+     *
+     * @param interpolator the interpolator to be used by each child animation of this Sequencer
+     */
+    @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
@@ -266,6 +291,62 @@
     }
 
     /**
+     * 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 Sequencer. This value may
+     * be less than 0, which indicates that no duration has been set on this Sequencer
+     * 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 Sequencer.
+     */
+    @Override
+    public long getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Sets the length of each of the current child animations of this Sequencer. By default,
+     * each child animation will use its own duration. If the duration is set on the Sequencer,
+     * then each child animation inherits this duration.
+     *
+     * @param duration The length of the animation, in milliseconds, of each of the child
+     * animations of this Sequencer.
+     */
+    @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 Sequencer 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
@@ -285,7 +366,7 @@
         // 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);
@@ -302,9 +383,22 @@
             node.animation.addListener(mSequenceListener);
         }
         // 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
+            Animator delayAnim = new Animator(mStartDelay, 0f, 1f);
+            delayAnim.addListener(new AnimatableListenerAdapter() {
+                public void onAnimationEnd(Animatable anim) {
+                    for (Node node : nodesToStart) {
+                        node.animation.start();
+                        mPlayingSet.add(node.animation);
+                    }
+                }
+            });
         }
         if (mListeners != null) {
             ArrayList<AnimatableListener> tmpListeners =
@@ -316,7 +410,7 @@
     }
 
     @Override
-    public Sequencer clone() throws CloneNotSupportedException {
+    public Sequencer clone() {
         final Sequencer anim = (Sequencer) super.clone();
         /*
          * The basic clone() operation copies all items. This doesn't work very well for
@@ -688,10 +782,14 @@
         }
 
         @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 = (Animatable) animation.clone();
+                return node;
+            } catch (CloneNotSupportedException e) {
+               throw new AssertionError();
+            }
         }
     }
 
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/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 33a1db8..460328d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -713,6 +713,21 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes an accelerometer.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
+
+    /**
+     * 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)
@@ -720,10 +735,10 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
-     * {@link #hasSystemFeature}: The device includes an accelerometer.
+     * {@link #hasSystemFeature}: The device includes a gyroscope.
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
+    public static final String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
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/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/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/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/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..47dae03 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;
 
 /**
@@ -1838,6 +1838,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}
@@ -2496,6 +2501,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.
@@ -4806,6 +4844,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 +5311,7 @@
      */
     public void setAlpha(float alpha) {
         mAlpha = alpha;
+        onSetAlpha((int) (alpha * 255));
         invalidate();
     }
 
@@ -5265,6 +5326,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 +5375,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 +5421,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 +5471,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 +5748,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);
@@ -7744,7 +7953,7 @@
      *
      * 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
@@ -7752,6 +7961,10 @@
      * @param b Bottom position, relative to parent
      */
     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 +7973,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 +7992,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
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9770313..649f3e7 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;
@@ -35,7 +36,6 @@
 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 +43,6 @@
 import android.view.animation.Transformation;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 
 /**
  * <p>
@@ -68,6 +67,7 @@
  * @attr ref android.R.styleable#ViewGroup_descendantFocusability
  */
 public abstract class ViewGroup extends View implements ViewParent, ViewManager {
+
     private static final boolean DBG = false;
 
     /**
@@ -300,6 +300,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();
@@ -1970,6 +1978,7 @@
             }
         } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
             child.onSetAlpha(255);
+            child.mPrivateFlags &= ~ALPHA_SET;
         }
 
         if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
@@ -2328,6 +2337,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 +2418,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 +2555,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 +2586,20 @@
         }
     }
 
+    /**
+     * 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.
+     */
+    public void setLayoutTransition(LayoutTransition transition) {
+        mTransition = transition;
+        mTransition.addTransitionListener(mLayoutTransitionListener);
+    }
+
     private void removeViewsInternal(int start, int count) {
         final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
         final boolean notifyListener = onHierarchyChangeListener != null;
@@ -2578,12 +2613,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 +2682,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 +2725,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 +3709,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 +4275,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 +4301,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 +4450,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;
         }
 
         static class TargetInfo {
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/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/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/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/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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2cd38cd..c2c2138 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1274,7 +1274,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"
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/public.xml b/core/res/res/values/public.xml
index e439593..26a75ee 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1324,12 +1324,12 @@
   <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="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. -->
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/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/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/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/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/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/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..0b6201c 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
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/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/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/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index b4b32bb..41828dc 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -356,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,
@@ -422,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/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/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/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 0c8326e..9016e68 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -21,6 +21,7 @@
 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;
@@ -44,6 +45,10 @@
     // 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;
 
@@ -69,6 +74,9 @@
     private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
                                             + MtpObjects.ObjectColumns.FORMAT + "=?";
 
+    private static final String[] DEVICE_PROPERTY_PROJECTION = new String[] { "_id", "value" };
+    private  static final String DEVICE_PROPERTY_WHERE = "code=?";
+
     private final MediaScanner mMediaScanner;
 
     static {
@@ -83,6 +91,7 @@
         mVolumeName = volumeName;
         mObjectsUri = MtpObjects.getContentUri(volumeName);
         mMediaScanner = new MediaScanner(context);
+        openDevicePropertiesDatabase(context);
     }
 
     @Override
@@ -94,6 +103,22 @@
         }
     }
 
+    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;
@@ -257,8 +282,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,
@@ -342,6 +369,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);
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/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index bcae913..c2f79e8 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -84,6 +84,7 @@
 
     mTrackMaxAmplitude = false;
     mMaxAmplitude = 0;
+    mInitialReadTimeUs = 0;
     mStartTimeUs = 0;
     int64_t startTimeUs;
     if (params && params->findInt64(kKeyTime, &startTimeUs)) {
@@ -210,6 +211,7 @@
         return NO_INIT;
     }
 
+    int64_t readTimeUs = systemTime() / 1000;
     *out = NULL;
 
     MediaBuffer *buffer;
@@ -223,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;
@@ -271,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;
@@ -309,7 +315,10 @@
             trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
         }
 
-        buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
+        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",
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/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
index 052c354..c05e3e5 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 = 0;
 
     if (mFrameCount == 0) {
         memcpy(outPtr, mAudioSpecificConfigData, 2);
@@ -238,9 +240,14 @@
             CHECK_EQ(align, 0);
 
             int64_t timeUs;
+            CHECK(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 +295,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) {
+        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..dab1390 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 = 0;
 
     while (mNumInputSamples < kNumSamplesPerFrame) {
         if (mInputBuffer == NULL) {
@@ -166,12 +168,16 @@
 
             size_t align = mInputBuffer->range_length() % sizeof(int16_t);
             CHECK_EQ(align, 0);
+            readFromSource = true;
 
             int64_t timeUs;
+            CHECK(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 +223,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) {
+        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..b62eb5b 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 = 0;
 
     while (mNumInputSamples < kNumSamplesPerFrame) {
         if (mInputBuffer == NULL) {
@@ -219,9 +221,14 @@
             CHECK_EQ(align, 0);
 
             int64_t timeUs;
+            CHECK(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 +283,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) {
+        buffer->meta_data()->setInt64(kKeyDriftTime, mediaTimeUs - wallClockTimeUs);
+    }
 
     *out = buffer;
     return OK;
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/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
index 0f0e7ce..8154ec0 100644
--- a/packages/SystemUI/res/anim/lights_out_in.xml
+++ b/packages/SystemUI/res/anim/lights_out_in.xml
@@ -18,8 +18,9 @@
     >
     <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_mediumAnimTime" 
+        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
index cb895d9..b4bc55a 100644
--- a/packages/SystemUI/res/anim/lights_out_out.xml
+++ b/packages/SystemUI/res/anim/lights_out_out.xml
@@ -18,8 +18,9 @@
     >
     <translate android:toYDelta="100%p" android:fromYDelta="0" 
         android:duration="@android:integer/config_mediumAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
         />
-    <alpha android:toAlpha="0.1" android:fromAlpha="1.0"
-        android:duration="@android:integer/config_mediumAnimTime" 
+    <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
index 48663f4..460fe50 100644
--- a/packages/SystemUI/res/anim/status_bar_in.xml
+++ b/packages/SystemUI/res/anim/status_bar_in.xml
@@ -17,9 +17,10 @@
 <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: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_mediumAnimTime" 
+        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
index b3f8953..6572ab4 100644
--- a/packages/SystemUI/res/anim/status_bar_out.xml
+++ b/packages/SystemUI/res/anim/status_bar_out.xml
@@ -17,9 +17,10 @@
 <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: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_mediumAnimTime" 
+        android:duration="@android:integer/config_longAnimTime" 
         />
 </set>
diff --git a/packages/SystemUI/res/drawable/sysbar_batterymini.xml b/packages/SystemUI/res/drawable/sysbar_batterymini.xml
index f7ba6b1..c7300e6 100644
--- a/packages/SystemUI/res/drawable/sysbar_batterymini.xml
+++ b/packages/SystemUI/res/drawable/sysbar_batterymini.xml
@@ -23,7 +23,7 @@
         android:maxLevel="100"
         android:gravity="left">
     <level-list>
-        <item android:maxLevel="15"  android:drawable="@drawable/sysbar_batterymini_red" />
-        <item android:maxLevel="101" android:drawable="@drawable/sysbar_batterymini_100" />
+        <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_signalmini.xml b/packages/SystemUI/res/drawable/sysbar_signalmini.xml
index ca6c9ed..598bf10 100644
--- a/packages/SystemUI/res/drawable/sysbar_signalmini.xml
+++ b/packages/SystemUI/res/drawable/sysbar_signalmini.xml
@@ -21,5 +21,5 @@
 <clip xmlns:android="http://schemas.android.com/apk/res/android"
         android:clipOrientation="horizontal"
         android:gravity="right"
-        android:maxLevel="100"
+        android:maxLevel="10000"
         android:drawable="@drawable/sysbar_signalmini_100" />
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 c28a3733..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;
@@ -158,52 +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 ConnectivityService getServiceInstance(Context context) {
-            ConnectivityThread thread = new ConnectivityThread(context);
-            thread.start();
-
-            synchronized (thread) {
-                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);
@@ -234,7 +203,6 @@
 
         mNetTrackers = new NetworkStateTracker[
                 ConnectivityManager.MAX_NETWORK_TYPE+1];
-        mHandler = new MyHandler();
 
         mNetworkPreference = getPersistedNetworkPreference();
 
@@ -1580,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/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 1d57738..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++) {
@@ -9383,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/tests/DumpRenderTree2/assets/run_apache2.py b/tests/DumpRenderTree2/assets/run_apache2.py
index e0e4be7..74ab850 100755
--- a/tests/DumpRenderTree2/assets/run_apache2.py
+++ b/tests/DumpRenderTree2/assets/run_apache2.py
@@ -92,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/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/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 700fb4e..beec8fe 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -83,16 +83,22 @@
      * 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() {
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);