Merge "Added MediaSourceSplitter to split single source to multiple ones."
diff --git a/Android.mk b/Android.mk
index a73767b..362a329 100644
--- a/Android.mk
+++ b/Android.mk
@@ -123,10 +123,6 @@
 	core/java/android/net/IThrottleManager.aidl \
 	core/java/android/os/IHardwareService.aidl \
 	core/java/android/os/IMessenger.aidl \
-	core/java/android/os/storage/IMountService.aidl \
-	core/java/android/os/storage/IMountServiceListener.aidl \
-	core/java/android/os/storage/IMountShutdownObserver.aidl \
-	core/java/android/os/storage/IObbActionListener.aidl \
 	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/INetStatService.aidl \
 	core/java/android/os/IPermissionController.aidl \
@@ -383,8 +379,6 @@
 # (see development/build/sdk.atree)
 web_docs_sample_code_flags := \
 		-hdf android.hasSamples 1 \
-		-samplecode $(sample_dir)/AccessibilityService \
-		            resources/samples/AccessibilityService "Accessibility Service" \
 		-samplecode $(sample_dir)/ApiDemos \
 		            resources/samples/ApiDemos "API Demos" \
 		-samplecode $(sample_dir)/BackupRestore \
@@ -529,7 +523,6 @@
 $(full_target): $(static_doc_index_redirect)
 $(full_target): $(framework_built)
 
-
 # ==== docs for the web (on the google app engine server) =======================
 include $(CLEAR_VARS)
 
@@ -556,7 +549,7 @@
 
 include $(BUILD_DROIDDOC)
 
-# explicitly specify that online-sdk depends on framework-res.
+# explicitly specify that online-sdk depends on framework-res and any generated docs
 $(full_target): framework-res-package-target
 
 # ==== docs that have all of the stuff that's @hidden =======================
diff --git a/CleanSpec.mk b/CleanSpec.mk
index f73e4d5..e961a5a 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -76,6 +76,8 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/libhwui.so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libhwui.so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwui.so)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/storage/*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content/IClipboard.P)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.xml b/api/current.xml
index d826416..3120561 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -4761,17 +4761,6 @@
  visibility="public"
 >
 </field>
-<field name="heavyWeight"
- type="int"
- transient="false"
- volatile="false"
- value="16843456"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="height"
  type="int"
  transient="false"
@@ -6708,6 +6697,17 @@
  visibility="public"
 >
 </field>
+<field name="listChoiceBackgroundIndicator"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843568"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="listChoiceIndicatorMultiple"
  type="int"
  transient="false"
@@ -15715,6 +15715,17 @@
  visibility="public"
 >
 </field>
+<field name="simple_selectable_list_item"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17367074"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="simple_spinner_dropdown_item"
  type="int"
  transient="false"
@@ -16663,7 +16674,7 @@
  visibility="public"
 >
 </field>
-<field name="Theme_Holo_NoTitleBar"
+<field name="Theme_Holo_NoActionBar"
  type="int"
  transient="false"
  volatile="false"
@@ -16674,7 +16685,7 @@
  visibility="public"
 >
 </field>
-<field name="Theme_Holo_NoTitleBar_Fullscreen"
+<field name="Theme_Holo_NoActionBar_Fullscreen"
  type="int"
  transient="false"
  volatile="false"
@@ -16718,7 +16729,7 @@
  visibility="public"
 >
 </field>
-<field name="Theme_Light_Holo_NoTitleBar"
+<field name="Theme_Light_Holo_NoActionBar"
  type="int"
  transient="false"
  volatile="false"
@@ -16729,7 +16740,7 @@
  visibility="public"
 >
 </field>
-<field name="Theme_Light_Holo_NoTitleBar_Fullscreen"
+<field name="Theme_Light_Holo_NoActionBar_Fullscreen"
  type="int"
  transient="false"
  volatile="false"
@@ -19779,6 +19790,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="java.lang.Cloneable">
+</implements>
 <constructor name="Animatable"
  type="android.animation.Animatable"
  static="false"
@@ -19811,6 +19824,19 @@
  visibility="public"
 >
 </method>
+<method name="clone"
+ return="android.animation.Animatable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="CloneNotSupportedException" type="java.lang.CloneNotSupportedException">
+</exception>
+</method>
 <method name="end"
  return="void"
  abstract="false"
@@ -19833,6 +19859,17 @@
  visibility="public"
 >
 </method>
+<method name="isRunning"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="removeAllListeners"
  return="void"
  abstract="false"
@@ -19929,6 +19966,40 @@
 </parameter>
 </method>
 </interface>
+<class name="AnimatableInflater"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AnimatableInflater"
+ type="android.animation.AnimatableInflater"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="loadAnimatable"
+ return="android.animation.Animatable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="id" type="int">
+</parameter>
+<exception name="Resources.NotFoundException" type="android.content.res.Resources.NotFoundException">
+</exception>
+</method>
+</class>
 <class name="AnimatableListenerAdapter"
  extends="java.lang.Object"
  abstract="true"
@@ -20015,10 +20086,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="attrs" type="android.util.AttributeSet">
-</parameter>
 </constructor>
 <constructor name="Animator"
  type="android.animation.Animator"
@@ -20080,6 +20147,17 @@
  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="getFrameDelay"
  return="long"
  abstract="false"
@@ -20091,6 +20169,17 @@
  visibility="public"
 >
 </method>
+<method name="getInterpolator"
+ return="android.view.animation.Interpolator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getRepeatCount"
  return="int"
  abstract="false"
@@ -20172,6 +20261,19 @@
 <parameter name="playTime" 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="duration" type="long">
+</parameter>
+</method>
 <method name="setEvaluator"
  return="void"
  abstract="false"
@@ -20594,10 +20696,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="attrs" type="android.util.AttributeSet">
-</parameter>
 </constructor>
 <constructor name="PropertyAnimator"
  type="android.animation.PropertyAnimator"
@@ -20870,6 +20968,17 @@
  visibility="public"
 >
 </method>
+<method name="isRunning"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="play"
  return="android.animation.Sequencer.Builder"
  abstract="false"
@@ -22227,17 +22336,6 @@
  visibility="public"
 >
 </method>
-<method name="isImmersive"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="isTaskRoot"
  return="boolean"
  abstract="false"
@@ -22256,7 +22354,7 @@
  synchronized="false"
  static="false"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="uri" type="android.net.Uri">
@@ -23269,19 +23367,6 @@
 <parameter name="uri" type="android.net.Uri">
 </parameter>
 </method>
-<method name="setImmersive"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="i" type="boolean">
-</parameter>
-</method>
 <method name="setIntent"
  return="void"
  abstract="false"
@@ -23643,7 +23728,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="c" type="android.database.Cursor">
@@ -23688,7 +23773,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="c" type="android.database.Cursor">
@@ -27469,6 +27554,17 @@
  visibility="public"
 >
 </method>
+<method name="getShowsDialog"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getTheme"
  return="int"
  abstract="false"
@@ -27532,6 +27628,19 @@
 <parameter name="cancelable" type="boolean">
 </parameter>
 </method>
+<method name="setShowsDialog"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="showsDialog" type="boolean">
+</parameter>
+</method>
 <method name="setStyle"
  return="void"
  abstract="false"
@@ -27554,7 +27663,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="activity" type="android.app.Activity">
@@ -27563,6 +27672,21 @@
 </parameter>
 </method>
 <method name="show"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="manager" type="android.app.FragmentManager">
+</parameter>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
+<method name="show"
  return="int"
  abstract="false"
  native="false"
@@ -27572,8 +27696,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="activity" type="android.app.Activity">
-</parameter>
 <parameter name="transaction" type="android.app.FragmentTransaction">
 </parameter>
 <parameter name="tag" type="java.lang.String">
@@ -39136,6 +39258,436 @@
 </parameter>
 </method>
 </class>
+<class name="ClipData"
+ extends="android.content.ClipDescription"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ClipData"
+ type="android.content.ClipData"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="mimeTypes" type="java.lang.String[]">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="item" type="android.content.ClipData.Item">
+</parameter>
+</constructor>
+<method name="addItem"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="item" type="android.content.ClipData.Item">
+</parameter>
+</method>
+<method name="getIcon"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getItem"
+ return="android.content.ClipData.Item"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
+<method name="getItemCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newIntent"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="newPlainText"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="newRawUri"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="newUri"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resolver" type="android.content.ContentResolver">
+</parameter>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="ClipData.Item"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ClipData.Item"
+ type="android.content.ClipData.Item"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</constructor>
+<constructor name="ClipData.Item"
+ type="android.content.ClipData.Item"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</constructor>
+<constructor name="ClipData.Item"
+ type="android.content.ClipData.Item"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</constructor>
+<constructor name="ClipData.Item"
+ type="android.content.ClipData.Item"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</constructor>
+<method name="coerceToText"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="getIntent"
+ return="android.content.Intent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getText"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="ClipDescription"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="ClipDescription"
+ type="android.content.ClipDescription"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="mimeTypes" type="java.lang.String[]">
+</parameter>
+</constructor>
+<constructor name="ClipDescription"
+ type="android.content.ClipDescription"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="android.content.ClipDescription">
+</parameter>
+</constructor>
+<method name="compareMimeTypes"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="concreteType" type="java.lang.String">
+</parameter>
+<parameter name="desiredType" type="java.lang.String">
+</parameter>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="filterMimeTypes"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="getLabel"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMimeType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
+<method name="getMimeTypeCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasMimeType"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MIMETYPE_TEXT_INTENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;text/vnd.android.intent&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MIMETYPE_TEXT_PLAIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;text/plain&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MIMETYPE_TEXT_URILIST"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;text/uri-list&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="ClipboardManager"
  extends="android.text.ClipboardManager"
  abstract="false"
@@ -39158,7 +39710,18 @@
 </parameter>
 </method>
 <method name="getPrimaryClip"
- return="android.content.ClippedData"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPrimaryClipDescription"
+ return="android.content.ClipDescription"
  abstract="false"
  native="false"
  synchronized="false"
@@ -39224,7 +39787,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="clip" type="android.content.ClippedData">
+<parameter name="clip" type="android.content.ClipData">
 </parameter>
 </method>
 <method name="setText"
@@ -39260,225 +39823,6 @@
 >
 </method>
 </interface>
-<class name="ClippedData"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.Parcelable">
-</implements>
-<constructor name="ClippedData"
- type="android.content.ClippedData"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="label" type="java.lang.CharSequence">
-</parameter>
-<parameter name="icon" type="android.graphics.Bitmap">
-</parameter>
-<parameter name="item" type="android.content.ClippedData.Item">
-</parameter>
-</constructor>
-<method name="addItem"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="item" type="android.content.ClippedData.Item">
-</parameter>
-</method>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getIcon"
- return="android.graphics.Bitmap"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getItem"
- return="android.content.ClippedData.Item"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="index" type="int">
-</parameter>
-</method>
-<method name="getItemCount"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getLabel"
- return="java.lang.CharSequence"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="writeToParcel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dest" type="android.os.Parcel">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="ClippedData.Item"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="ClippedData.Item"
- type="android.content.ClippedData.Item"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="text" type="java.lang.CharSequence">
-</parameter>
-</constructor>
-<constructor name="ClippedData.Item"
- type="android.content.ClippedData.Item"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="intent" type="android.content.Intent">
-</parameter>
-</constructor>
-<constructor name="ClippedData.Item"
- type="android.content.ClippedData.Item"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-</constructor>
-<constructor name="ClippedData.Item"
- type="android.content.ClippedData.Item"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="text" type="java.lang.CharSequence">
-</parameter>
-<parameter name="intent" type="android.content.Intent">
-</parameter>
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-</constructor>
-<method name="coerceToText"
- return="java.lang.CharSequence"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-</method>
-<method name="getIntent"
- return="android.content.Intent"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getText"
- return="java.lang.CharSequence"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getUri"
- return="android.net.Uri"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <interface name="ComponentCallbacks"
  abstract="true"
  static="false"
@@ -39802,21 +40146,6 @@
 <parameter name="values" type="android.content.ContentValues[]">
 </parameter>
 </method>
-<method name="compareMimeTypes"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="concreteType" type="java.lang.String">
-</parameter>
-<parameter name="desiredType" type="java.lang.String">
-</parameter>
-</method>
 <method name="delete"
  return="int"
  abstract="true"
@@ -51073,6 +51402,17 @@
 <parameter name="key" type="java.lang.String">
 </parameter>
 </method>
+<method name="startCommit"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 </interface>
 <interface name="SharedPreferences.OnSharedPreferenceChangeListener"
  abstract="true"
@@ -52602,17 +52942,6 @@
  visibility="public"
 >
 </field>
-<field name="FLAG_HEAVY_WEIGHT"
- type="int"
- transient="false"
- volatile="false"
- value="1048576"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FLAG_KILL_AFTER_RESTORE"
  type="int"
  transient="false"
@@ -62814,6 +63143,42 @@
 <parameter name="table" type="java.lang.String">
 </parameter>
 </method>
+<method name="queryNumEntries"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="db" type="android.database.sqlite.SQLiteDatabase">
+</parameter>
+<parameter name="table" type="java.lang.String">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+</method>
+<method name="queryNumEntries"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="db" type="android.database.sqlite.SQLiteDatabase">
+</parameter>
+<parameter name="table" type="java.lang.String">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
 <method name="readExceptionFromParcel"
  return="void"
  abstract="false"
@@ -84533,6 +84898,17 @@
  visibility="public"
 >
 </field>
+<field name="TYPE_GRAVITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TYPE_GYROSCOPE"
  type="int"
  transient="false"
@@ -84555,6 +84931,17 @@
  visibility="public"
 >
 </field>
+<field name="TYPE_LINEAR_ACCELERATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TYPE_MAGNETIC_FIELD"
  type="int"
  transient="false"
@@ -84599,6 +84986,17 @@
  visibility="public"
 >
 </field>
+<field name="TYPE_ROTATION_VECTOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="11"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TYPE_TEMPERATURE"
  type="int"
  transient="false"
@@ -84758,6 +85156,23 @@
 <parameter name="p" type="float">
 </parameter>
 </method>
+<method name="getAngleChange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="angleChange" type="float[]">
+</parameter>
+<parameter name="R" type="float[]">
+</parameter>
+<parameter name="prevR" type="float[]">
+</parameter>
+</method>
 <method name="getDefaultSensor"
  return="android.hardware.Sensor"
  abstract="false"
@@ -84799,6 +85214,21 @@
 <parameter name="values" type="float[]">
 </parameter>
 </method>
+<method name="getQuaternionFromVector"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="Q" type="float[]">
+</parameter>
+<parameter name="rv" type="float[]">
+</parameter>
+</method>
 <method name="getRotationMatrix"
  return="boolean"
  abstract="false"
@@ -84818,6 +85248,21 @@
 <parameter name="geomagnetic" type="float[]">
 </parameter>
 </method>
+<method name="getRotationMatrixFromVector"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="R" type="float[]">
+</parameter>
+<parameter name="rotationVector" type="float[]">
+</parameter>
+</method>
 <method name="getSensorList"
  return="java.util.List&lt;android.hardware.Sensor&gt;"
  abstract="false"
@@ -99742,7 +100187,7 @@
 >
 </field>
 <field name="PRESET_LARGEHALL"
- type="int"
+ type="short"
  transient="false"
  volatile="false"
  value="5"
@@ -99753,7 +100198,7 @@
 >
 </field>
 <field name="PRESET_LARGEROOM"
- type="int"
+ type="short"
  transient="false"
  volatile="false"
  value="3"
@@ -99764,7 +100209,7 @@
 >
 </field>
 <field name="PRESET_MEDIUMHALL"
- type="int"
+ type="short"
  transient="false"
  volatile="false"
  value="4"
@@ -99775,7 +100220,7 @@
 >
 </field>
 <field name="PRESET_MEDIUMROOM"
- type="int"
+ type="short"
  transient="false"
  volatile="false"
  value="2"
@@ -99786,7 +100231,7 @@
 >
 </field>
 <field name="PRESET_NONE"
- type="int"
+ type="short"
  transient="false"
  volatile="false"
  value="0"
@@ -99797,7 +100242,7 @@
 >
 </field>
 <field name="PRESET_PLATE"
- type="int"
+ type="short"
  transient="false"
  volatile="false"
  value="6"
@@ -99808,7 +100253,7 @@
 >
 </field>
 <field name="PRESET_SMALLROOM"
- type="int"
+ type="short"
  transient="false"
  volatile="false"
  value="1"
@@ -105147,6 +105592,17 @@
 <parameter name="key" type="java.lang.String">
 </parameter>
 </method>
+<method name="getQueryParameterNames"
+ return="java.util.Set&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getQueryParameters"
  return="java.util.List&lt;java.lang.String&gt;"
  abstract="false"
@@ -105393,6 +105849,17 @@
  visibility="public"
 >
 </method>
+<method name="clearQuery"
+ return="android.net.Uri.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="encodedAuthority"
  return="android.net.Uri.Builder"
  abstract="false"
@@ -136157,7 +136624,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<method name="getThreadBlockingPolicy"
+<method name="allowThreadDiskReads"
  return="int"
  abstract="false"
  native="false"
@@ -136168,7 +136635,29 @@
  visibility="public"
 >
 </method>
-<method name="setThreadBlockingPolicy"
+<method name="allowThreadDiskWrites"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getThreadPolicy"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setThreadPolicy"
  return="void"
  abstract="false"
  native="false"
@@ -138900,6 +139389,17 @@
  visibility="public"
 >
 </method>
+<method name="onGetNewHeader"
+ return="android.preference.PreferenceActivity.Header"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="onHeaderClick"
  return="void"
  abstract="false"
@@ -138967,6 +139467,19 @@
 <parameter name="preference" type="android.preference.Preference">
 </parameter>
 </method>
+<method name="setListFooter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
 <method name="setPreferenceScreen"
  return="void"
  abstract="false"
@@ -139100,6 +139613,16 @@
  visibility="public"
 >
 </field>
+<field name="intent"
+ type="android.content.Intent"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="summary"
  type="java.lang.CharSequence"
  transient="false"
@@ -145232,6 +145755,39 @@
 >
 </field>
 </class>
+<class name="ContactsContract.CommonDataKinds.SipAddress"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.ContactsContract.DataColumnsWithJoins">
+</implements>
+<field name="CONTENT_ITEM_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;vnd.android.cursor.item/sip_address&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SIP_ADDRESS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;data1&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="ContactsContract.CommonDataKinds.StructuredName"
  extends="java.lang.Object"
  abstract="false"
@@ -145661,6 +146217,91 @@
 >
 </field>
 </class>
+<interface name="ContactsContract.ContactNameColumns"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<field name="DISPLAY_NAME_ALTERNATIVE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;display_name_alt&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY_NAME_PRIMARY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;display_name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY_NAME_SOURCE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;display_name_source&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHONETIC_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;phonetic_name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHONETIC_NAME_STYLE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;phonetic_name_style&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SORT_KEY_ALTERNATIVE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;sort_key_alt&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SORT_KEY_PRIMARY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;sort_key&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
 <interface name="ContactsContract.ContactOptionsColumns"
  abstract="true"
  static="true"
@@ -145819,6 +146460,8 @@
 >
 <implements name="android.provider.BaseColumns">
 </implements>
+<implements name="android.provider.ContactsContract.ContactNameColumns">
+</implements>
 <implements name="android.provider.ContactsContract.ContactOptionsColumns">
 </implements>
 <implements name="android.provider.ContactsContract.ContactStatusColumns">
@@ -146052,6 +146695,68 @@
 >
 </field>
 </class>
+<class name="ContactsContract.Contacts.Entity"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.BaseColumns">
+</implements>
+<implements name="android.provider.ContactsContract.BaseSyncColumns">
+</implements>
+<implements name="android.provider.ContactsContract.ContactNameColumns">
+</implements>
+<implements name="android.provider.ContactsContract.ContactOptionsColumns">
+</implements>
+<implements name="android.provider.ContactsContract.ContactStatusColumns">
+</implements>
+<implements name="android.provider.ContactsContract.ContactsColumns">
+</implements>
+<implements name="android.provider.ContactsContract.DataColumns">
+</implements>
+<implements name="android.provider.ContactsContract.RawContactsColumns">
+</implements>
+<implements name="android.provider.ContactsContract.StatusColumns">
+</implements>
+<implements name="android.provider.ContactsContract.SyncColumns">
+</implements>
+<field name="CONTENT_DIRECTORY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;entities&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DATA_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;data_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RAW_CONTACT_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;raw_contact_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="ContactsContract.Contacts.Photo"
  extends="java.lang.Object"
  abstract="false"
@@ -146489,6 +147194,8 @@
 >
 <implements name="android.provider.BaseColumns">
 </implements>
+<implements name="android.provider.ContactsContract.ContactNameColumns">
+</implements>
 <implements name="android.provider.ContactsContract.ContactOptionsColumns">
 </implements>
 <implements name="android.provider.ContactsContract.ContactStatusColumns">
@@ -146734,6 +147441,154 @@
 >
 </field>
 </class>
+<interface name="ContactsContract.DisplayNameSources"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="EMAIL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NICKNAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="35"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ORGANIZATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="30"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="20"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STRUCTURED_NAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="40"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UNDEFINED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
+<interface name="ContactsContract.FullNameStyle"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="CHINESE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CJK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="JAPANESE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KOREAN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UNDEFINED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WESTERN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
 <class name="ContactsContract.Groups"
  extends="java.lang.Object"
  abstract="false"
@@ -147398,6 +148253,58 @@
 >
 </field>
 </interface>
+<interface name="ContactsContract.PhoneticNameStyle"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="JAPANESE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KOREAN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PINYIN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UNDEFINED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
 <class name="ContactsContract.Presence"
  extends="android.provider.ContactsContract.StatusUpdates"
  abstract="false"
@@ -147580,6 +148487,8 @@
 >
 <implements name="android.provider.BaseColumns">
 </implements>
+<implements name="android.provider.ContactsContract.ContactNameColumns">
+</implements>
 <implements name="android.provider.ContactsContract.ContactOptionsColumns">
 </implements>
 <implements name="android.provider.ContactsContract.RawContactsColumns">
@@ -205555,23 +206464,6 @@
 <exception name="Resources.NotFoundException" type="android.content.res.Resources.NotFoundException">
 </exception>
 </method>
-<method name="loadAnimator"
- return="android.animation.Animatable"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="id" type="int">
-</parameter>
-<exception name="Resources.NotFoundException" type="android.content.res.Resources.NotFoundException">
-</exception>
-</method>
 <method name="loadInterpolator"
  return="android.view.animation.Interpolator"
  abstract="false"
@@ -207255,6 +208147,19 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
+<method name="getSelectedText"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
 <method name="getTextAfterCursor"
  return="java.lang.CharSequence"
  abstract="false"
@@ -207365,6 +208270,21 @@
 <parameter name="event" type="android.view.KeyEvent">
 </parameter>
 </method>
+<method name="setComposingRegion"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+</method>
 <method name="setComposingSpans"
  return="void"
  abstract="false"
@@ -208359,6 +209279,19 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
+<method name="getSelectedText"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
 <method name="getTextAfterCursor"
  return="java.lang.CharSequence"
  abstract="true"
@@ -208456,6 +209389,21 @@
 <parameter name="event" type="android.view.KeyEvent">
 </parameter>
 </method>
+<method name="setComposingRegion"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+</method>
 <method name="setComposingText"
  return="boolean"
  abstract="true"
@@ -208648,6 +209596,19 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
+<method name="getSelectedText"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
 <method name="getTextAfterCursor"
  return="java.lang.CharSequence"
  abstract="false"
@@ -208745,6 +209706,21 @@
 <parameter name="event" type="android.view.KeyEvent">
 </parameter>
 </method>
+<method name="setComposingRegion"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+</method>
 <method name="setComposingText"
  return="boolean"
  abstract="false"
@@ -225181,6 +226157,19 @@
 <parameter name="modal" type="boolean">
 </parameter>
 </method>
+<method name="setOnDismissListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.PopupWindow.OnDismissListener">
+</parameter>
+</method>
 <method name="setOnItemClickListener"
  return="void"
  abstract="false"
@@ -228576,6 +229565,21 @@
 <parameter name="value" type="double">
 </parameter>
 </method>
+<method name="setEmptyView"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="emptyViewId" type="int">
+</parameter>
+</method>
 <method name="setFloat"
  return="void"
  abstract="false"
@@ -234423,7 +235427,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="offset" type="int">
+<parameter name="x" type="int">
+</parameter>
+<parameter name="y" type="int">
 </parameter>
 </method>
 <field name="FADE_OUT_DURATION"
@@ -257994,7 +259000,7 @@
 <method name="copySign"
  return="double"
  abstract="false"
- native="true"
+ native="false"
  synchronized="false"
  static="true"
  final="false"
@@ -258009,7 +259015,7 @@
 <method name="copySign"
  return="float"
  abstract="false"
- native="true"
+ native="false"
  synchronized="false"
  static="true"
  final="false"
@@ -269429,7 +270435,7 @@
 >
 <parameter name="numBits" type="int">
 </parameter>
-<parameter name="rnd" type="java.util.Random">
+<parameter name="random" type="java.util.Random">
 </parameter>
 </constructor>
 <constructor name="BigInteger"
@@ -269443,7 +270449,7 @@
 </parameter>
 <parameter name="certainty" type="int">
 </parameter>
-<parameter name="rnd" type="java.util.Random">
+<parameter name="unused" type="java.util.Random">
 </parameter>
 </constructor>
 <constructor name="BigInteger"
@@ -269453,7 +270459,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.lang.String">
+<parameter name="value" type="java.lang.String">
 </parameter>
 </constructor>
 <constructor name="BigInteger"
@@ -269463,7 +270469,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.lang.String">
+<parameter name="value" type="java.lang.String">
 </parameter>
 <parameter name="radix" type="int">
 </parameter>
@@ -269487,7 +270493,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="byte[]">
+<parameter name="value" type="byte[]">
 </parameter>
 </constructor>
 <method name="abs"
@@ -269511,7 +270517,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="and"
@@ -269524,7 +270530,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="andNot"
@@ -269537,7 +270543,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="bitCount"
@@ -269585,7 +270591,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="divide"
@@ -269659,7 +270665,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="getLowestSetBit"
@@ -269718,7 +270724,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="min"
@@ -269731,7 +270737,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="mod"
@@ -269785,7 +270791,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="negate"
@@ -269831,7 +270837,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="pow"
@@ -269859,7 +270865,7 @@
 >
 <parameter name="bitLength" type="int">
 </parameter>
-<parameter name="rnd" type="java.util.Random">
+<parameter name="unused" type="java.util.Random">
 </parameter>
 </method>
 <method name="remainder"
@@ -269935,7 +270941,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="testBit"
@@ -269985,7 +270991,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="long">
+<parameter name="value" type="long">
 </parameter>
 </method>
 <method name="xor"
@@ -269998,7 +271004,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="java.math.BigInteger">
+<parameter name="value" type="java.math.BigInteger">
 </parameter>
 </method>
 <field name="ONE"
@@ -276445,6 +277451,19 @@
 <parameter name="p" type="java.security.Permission">
 </parameter>
 </method>
+<method name="isValidIP6Address"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ipAddress" type="java.lang.String">
+</parameter>
+</method>
 </class>
 <class name="SocketTimeoutException"
  extends="java.io.InterruptedIOException"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 0f343ff..4265cc5 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -142,6 +142,11 @@
                 String value = nextArgRequired();
                 intent.putExtra(key, Integer.valueOf(value));
                 hasIntentInfo = true;
+            } else if (opt.equals("--el")) {
+                String key = nextArgRequired();
+                String value = nextArgRequired();
+                intent.putExtra(key, Long.valueOf(value));
+                hasIntentInfo = true;
             } else if (opt.equals("--ez")) {
                 String key = nextArgRequired();
                 String value = nextArgRequired();
@@ -627,6 +632,7 @@
                 "        [--esn <EXTRA_KEY> ...]\n" +
                 "        [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
                 "        [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
+                "        [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
                 "        [-n <COMPONENT>] [-f <FLAGS>]\n" +
                 "        [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
                 "        [--debug-log-resolution]\n" +
diff --git a/cmds/screenshot/screenshot.c b/cmds/screenshot/screenshot.c
index 46e6507..048636c 100644
--- a/cmds/screenshot/screenshot.c
+++ b/cmds/screenshot/screenshot.c
@@ -88,13 +88,61 @@
     png_destroy_write_struct(&png, NULL);
 }
 
+void fork_sound(const char* path) {
+    pid_t pid = fork();
+    if (pid == 0) {
+        execl("/system/bin/stagefright", "stagefright", "-o", "-a", path, NULL);
+    }
+}
+
+void usage() {
+    fprintf(stderr,
+            "usage: screenshot [-s soundfile] filename.png\n"
+            "   -s: play a sound effect to signal success\n"
+            "   -i: autoincrement to avoid overwriting filename.png\n"
+    );
+}
+
 int main(int argc, char**argv) {
     FILE *png = NULL;
     FILE *fb_in = NULL;
-    if (argc < 2) {
-        fprintf(stderr, "usage: screenshot filename.png\n");
-        exit(1);
+    char outfile[PATH_MAX] = "";
+
+    char * soundfile = NULL;
+    int do_increment = 0;
+
+    int c;
+    while ((c = getopt(argc, argv, "s:i")) != -1) {
+        switch (c) {
+            case 's': soundfile = optarg; break;
+            case 'i': do_increment = 1; break;
+            case '?':
+            case 'h':
+                usage(); exit(1);
+        }
     }
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 1) {
+        usage(); exit(1);
+    }
+
+    strlcpy(outfile, argv[0], PATH_MAX);
+    if (do_increment) {
+        struct stat st;
+        char base[PATH_MAX] = "";
+        int i = 0;
+        while (stat(outfile, &st) == 0) {
+            if (!base[0]) {
+                char *p = strrchr(outfile, '.');
+                if (p) *p = '\0';
+                strcpy(base, outfile);
+            }
+            snprintf(outfile, PATH_MAX, "%s-%d.png", base, ++i);
+        }
+    }
+
     fb_in = fopen("/dev/graphics/fb0", "r");
     if (!fb_in) {
         fprintf(stderr, "error: could not read framebuffer\n");
@@ -106,13 +154,18 @@
     setgroups(sizeof(groups)/sizeof(groups[0]), groups);
     setuid(AID_SHELL);
 
-    png = fopen(argv[1], "w");
+    png = fopen(outfile, "w");
     if (!png) {
-        fprintf(stderr, "error: writing file %s: %s\n", argv[1], strerror(errno));
+        fprintf(stderr, "error: writing file %s: %s\n",
+                outfile, strerror(errno));
         exit(1);
     }
 
     take_screenshot(fb_in, png);
 
+    if (soundfile) {
+        fork_sound(soundfile);
+    }
+
     exit(0);
 }
diff --git a/core/java/android/animation/Animatable.java b/core/java/android/animation/Animatable.java
index 68415f0..d6cf7c0 100644
--- a/core/java/android/animation/Animatable.java
+++ b/core/java/android/animation/Animatable.java
@@ -22,7 +22,7 @@
  * This is the superclass for classes which provide basic support for animations which can be
  * started, ended, and have <code>AnimatableListeners</code> added to them.
  */
-public abstract class Animatable {
+public abstract class Animatable implements Cloneable {
 
 
     /**
@@ -56,6 +56,13 @@
     public void end() {
     }
 
+
+    /**
+     * Returns whether this Animatable is currently running (having been started and not yet ended).
+     * @return Whether the Animatable is running.
+     */
+    public abstract boolean isRunning();
+
     /**
      * Adds a listener to the set of listeners that are sent events through the life of an
      * animation, such as start, repeat, and end.
@@ -107,6 +114,20 @@
         }
     }
 
+    @Override
+    public Animatable clone() throws CloneNotSupportedException {
+        final Animatable anim = (Animatable) super.clone();
+        if (mListeners != null) {
+            ArrayList<AnimatableListener> oldListeners = mListeners;
+            anim.mListeners = new ArrayList<AnimatableListener>();
+            int numListeners = oldListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                anim.mListeners.add(oldListeners.get(i));
+            }
+        }
+        return anim;
+    }
+
     /**
      * <p>An animation listener receives notifications from an animation.
      * Notifications indicate animation related events, such as the end or the
diff --git a/core/java/android/animation/AnimatableInflater.java b/core/java/android/animation/AnimatableInflater.java
new file mode 100644
index 0000000..88fa77e
--- /dev/null
+++ b/core/java/android/animation/AnimatableInflater.java
@@ -0,0 +1,269 @@
+/*
+ * 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.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.content.res.Resources.NotFoundException;
+import android.util.AttributeSet;
+import android.util.Xml;
+import android.view.animation.AnimationUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * This class is used to instantiate menu XML files into Animatable objects.
+ * <p>
+ * For performance reasons, menu inflation relies heavily on pre-processing of
+ * XML files that is done at build time. Therefore, it is not currently possible
+ * to use MenuInflater with an XmlPullParser over a plain XML file at runtime;
+ * it only works with an XmlPullParser returned from a compiled resource (R.
+ * <em>something</em> file.)
+ */
+public class AnimatableInflater {
+
+    /**
+     * These flags are used when parsing Sequencer objects
+     */
+    private static final int TOGETHER = 0;
+    private static final int SEQUENTIALLY = 1;
+
+    /**
+     * Enum values used in XML attributes to indicate the value for mValueType
+     */
+    private static final int VALUE_TYPE_FLOAT       = 0;
+    private static final int VALUE_TYPE_INT         = 1;
+    private static final int VALUE_TYPE_DOUBLE      = 2;
+    private static final int VALUE_TYPE_COLOR       = 3;
+    private static final int VALUE_TYPE_CUSTOM      = 4;
+
+    /**
+     * Loads an {@link Animatable} object from a resource
+     *
+     * @param context Application context used to access resources
+     * @param id The resource id of the animation to load
+     * @return The animatable object reference by the specified id
+     * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
+     */
+    public static Animatable loadAnimatable(Context context, int id)
+            throws NotFoundException {
+
+        XmlResourceParser parser = null;
+        try {
+            parser = context.getResources().getAnimation(id);
+            return createAnimatableFromXml(context, parser);
+        } catch (XmlPullParserException ex) {
+            Resources.NotFoundException rnf =
+                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
+                    Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } catch (IOException ex) {
+            Resources.NotFoundException rnf =
+                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
+                    Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+
+        return createAnimatableFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
+    }
+
+    private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser,
+            AttributeSet attrs, Sequencer parent, int sequenceOrdering)
+            throws XmlPullParserException, IOException {
+
+        Animatable anim = null;
+        ArrayList<Animatable> childAnims = null;
+
+        // Make sure we are on a start tag.
+        int type;
+        int depth = parser.getDepth();
+
+        while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+               && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String  name = parser.getName();
+
+            if (name.equals("property")) {
+                anim = loadPropertyAnimator(c, attrs);
+            } else if (name.equals("animator")) {
+                anim = loadAnimator(c, attrs, null);
+            } else if (name.equals("sequencer")) {
+                anim = new Sequencer();
+                TypedArray a = c.obtainStyledAttributes(attrs,
+                        com.android.internal.R.styleable.Sequencer);
+                int ordering = a.getInt(com.android.internal.R.styleable.Sequencer_ordering,
+                        TOGETHER);
+                createAnimatableFromXml(c, parser, attrs, (Sequencer) anim,  ordering);
+                a.recycle();
+            } else {
+                throw new RuntimeException("Unknown animator name: " + parser.getName());
+            }
+
+            if (parent != null) {
+                if (childAnims == null) {
+                    childAnims = new ArrayList<Animatable>();
+                }
+                childAnims.add(anim);
+            }
+        }
+        if (parent != null && childAnims != null) {
+            Animatable[] animsArray = new Animatable[childAnims.size()];
+            int index = 0;
+            for (Animatable a : childAnims) {
+                animsArray[index++] = a;
+            }
+            if (sequenceOrdering == TOGETHER) {
+                parent.playTogether(animsArray);
+            } else {
+                parent.playSequentially(animsArray);
+            }
+        }
+
+        return anim;
+
+    }
+
+    private static PropertyAnimator loadPropertyAnimator(Context context, AttributeSet attrs)
+            throws NotFoundException {
+
+        PropertyAnimator anim = new PropertyAnimator();
+
+        loadAnimator(context, attrs, anim);
+
+        TypedArray a =
+                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.PropertyAnimator);
+
+        String propertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName);
+
+        anim.setPropertyName(propertyName);
+
+        a.recycle();
+
+        return anim;
+    }
+
+    /**
+     * Creates a new animation whose parameters come from the specified context and
+     * attributes set.
+     *
+     * @param context the application environment
+     * @param attrs the set of attributes holding the animation parameters
+     */
+    private static Animator loadAnimator(Context context, AttributeSet attrs, Animator anim)
+            throws NotFoundException {
+
+        TypedArray a =
+                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator);
+
+        long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 0);
+
+        long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
+
+        int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
+                VALUE_TYPE_FLOAT);
+
+        Object valueFrom = null;
+        Object valueTo = null;
+        TypeEvaluator evaluator = null;
+
+        switch (valueType) {
+            case VALUE_TYPE_FLOAT:
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueFrom)) {
+                    valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+                }
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueTo)) {
+                    valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+                }
+                break;
+            case VALUE_TYPE_COLOR:
+                evaluator = new RGBEvaluator();
+                // fall through to pick up values
+            case VALUE_TYPE_INT:
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueFrom)) {
+                    valueFrom = a.getInteger(com.android.internal.R.styleable.Animator_valueFrom, 0);
+                }
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueTo)) {
+                    valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0);
+                }
+                break;
+            case VALUE_TYPE_DOUBLE:
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueFrom)) {
+                    valueFrom = (Double)((Float)(a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f))).doubleValue();
+                }
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueTo)) {
+                    valueTo = (Double)((Float)a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f)).doubleValue();
+                }
+                break;
+            case VALUE_TYPE_CUSTOM:
+                // TODO: How to get an 'Object' value?
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueFrom)) {
+                    valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+                }
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueTo)) {
+                    valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+                }
+                break;
+        }
+
+        if (anim == null) {
+            anim = new Animator(duration, valueFrom, valueTo);
+        } else {
+            anim.setDuration(duration);
+            anim.setValues(valueFrom, valueTo);
+        }
+
+        anim.setStartDelay(startDelay);
+
+        if (a.hasValue(com.android.internal.R.styleable.Animator_repeatCount)) {
+            anim.setRepeatCount(
+                    a.getInt(com.android.internal.R.styleable.Animator_repeatCount, 0));
+        }
+        if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) {
+            anim.setRepeatMode(
+                    a.getInt(com.android.internal.R.styleable.Animator_repeatMode,
+                            Animator.RESTART));
+        }
+        if (evaluator != null) {
+            anim.setEvaluator(evaluator);
+        }
+
+        final int resID =
+                a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
+        if (resID > 0) {
+            anim.setInterpolator(AnimationUtils.loadInterpolator(context, resID));
+        }
+        a.recycle();
+
+        return anim;
+    }
+}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 951d104..8b74658 100755
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -17,10 +17,9 @@
 package android.animation;
 
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
-import android.util.AttributeSet;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -69,19 +68,12 @@
     private static final int SEEKED     = 4; // Seeked to some time value
 
     /**
-     * Enum values used in XML attributes to indicate the value for mValueType
-     */
-    private static final int VALUE_TYPE_FLOAT       = 0;
-    private static final int VALUE_TYPE_INT         = 1;
-    private static final int VALUE_TYPE_DOUBLE      = 2;
-    private static final int VALUE_TYPE_COLOR       = 3;
-    private static final int VALUE_TYPE_CUSTOM      = 4;
-
-    /**
      * Internal variables
+     * NOTE: This object implements the clone() method, making a deep copy of any referenced
+     * objects. As other non-trivial fields are added to this class, make sure to add logic
+     * to clone() to make deep copies of them.
      */
 
-
     // The first time that the animation's animateFrame() method is called. This time is used to
     // determine elapsed time (and therefore the elapsed fraction) in subsequent calls
     // to animateFrame()
@@ -197,20 +189,13 @@
     /**
      * The property/value sets being animated.
      */
-    HashMap<String, PropertyValuesHolder> mValues;
+    PropertyValuesHolder[] mValues;
 
     /**
-     * This value is used in the simple/common case of animating just one value; the user
-     * may call getAnimatedValue(), which should return the value of the first (and only)
-     * ProeprtyValuesHolder animated value, which is looked up using this string.
+     * A hashmap of the PropertyValuesHolder objects. This map is used to lookup animated values
+     * by property name during calls to getAnimatedValue(String).
      */
-    String mFirstPropertyName;
-
-
-    /**
-     * The type of the values, as determined by the valueFrom/valueTo properties.
-     */
-    Class mValueType;
+    HashMap<String, PropertyValuesHolder> mValuesMap;
 
     /**
      * Public constants
@@ -233,76 +218,13 @@
     public static final int INFINITE = -1;
 
     /**
-     * Creates a new animation whose parameters come from the specified context and
-     * attributes set.
-     *
-     * @param context the application environment
-     * @param attrs the set of attributes holding the animation parameters
+     * Creates a new Animator object. This default constructor is primarily for
+     * use internally; the other constructors which take parameters are more generally
+     * useful.
      */
-    public Animator(Context context, AttributeSet attrs) {
-        TypedArray a =
-                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator);
-
-        mDuration = (long) a.getInt(com.android.internal.R.styleable.Animator_duration, 0);
-
-        mStartDelay = (long) a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
-
-        final int resID =
-                a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
-        if (resID > 0) {
-            setInterpolator(AnimationUtils.loadInterpolator(context, resID));
-        }
-        int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
-                VALUE_TYPE_FLOAT);
-
-        Object valueFrom = null;
-        Object valueTo = null;
-
-        switch (valueType) {
-            case VALUE_TYPE_FLOAT:
-                valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
-                valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
-                mValueType = float.class;
-                break;
-            case VALUE_TYPE_INT:
-                valueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0);
-                valueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0);
-                mValueType = int.class;
-                break;
-            case VALUE_TYPE_DOUBLE:
-                valueFrom = (double)
-                        a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
-                valueTo = (double)
-                        a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
-                mValueType = double.class;
-                break;
-            case VALUE_TYPE_COLOR:
-                valueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0);
-                valueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0);
-                setEvaluator(new RGBEvaluator());
-                mValueType = int.class;
-                break;
-            case VALUE_TYPE_CUSTOM:
-                // TODO: How to get an 'Object' value?
-                valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
-                valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
-                mValueType = Object.class;
-                break;
-        }
-
-        mValues = new HashMap<String, PropertyValuesHolder>(1);
-        mFirstPropertyName = "";
-        PropertyValuesHolder valuesHolder = new PropertyValuesHolder(mFirstPropertyName,
-                valueFrom, valueTo);
-        mValues.put(mFirstPropertyName, valuesHolder);
-
-        mRepeatCount = a.getInt(com.android.internal.R.styleable.Animator_repeatCount, mRepeatCount);
-        mRepeatMode = a.getInt(com.android.internal.R.styleable.Animator_repeatMode, RESTART);
-
-        a.recycle();
+    public Animator() {
     }
 
-
     /**
      * Constructs an Animator object with the specified duration and set of
      * values. If the values are a set of PropertyValuesHolder objects, then these objects
@@ -323,15 +245,11 @@
 
     public void setValues(PropertyValuesHolder... values) {
         int numValues = values.length;
-        mValues = new HashMap<String, PropertyValuesHolder>(numValues);
+        mValues = values;
+        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
         for (int i = 0; i < numValues; ++i) {
             PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
-            mValues.put(valuesHolder.getPropertyName(), valuesHolder);
-        }
-        if (numValues > 0 && values[0] != null) {
-            mFirstPropertyName = ((PropertyValuesHolder) values[0]).getPropertyName();
-        } else {
-            mFirstPropertyName = "";
+            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
         }
     }
 
@@ -348,26 +266,12 @@
      * @param values The set of values to animate between.
      */
     public void setValues(T... values) {
-        if (values[0] instanceof PropertyValuesHolder) {
-            int numValues = values.length;
-            mValues = new HashMap<String, PropertyValuesHolder>(numValues);
-            for (int i = 0; i < numValues; ++i) {
-                PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
-                mValues.put(valuesHolder.getPropertyName(), valuesHolder);
-            }
-            if (numValues > 0 && values[0] != null) {
-                mFirstPropertyName = ((PropertyValuesHolder) values[0]).getPropertyName();
-            } else {
-                mFirstPropertyName = "";
-            }
+        if (mValues == null || mValues.length == 0) {
+            setValues(new PropertyValuesHolder[]{
+                    new PropertyValuesHolder("", (Object[])values)});
         } else {
-            if (mValues == null) {
-                setValues(new PropertyValuesHolder[]{
-                        new PropertyValuesHolder("", (Object[])values)});
-            } else {
-                PropertyValuesHolder valuesHolder = mValues.get(mFirstPropertyName);
-                valuesHolder.setValues(values);
-            }
+            PropertyValuesHolder valuesHolder = mValues[0];
+            valuesHolder.setValues(values);
         }
     }
 
@@ -382,20 +286,40 @@
      *  that internal mechanisms for the animation are set up correctly.</p>
      */
     void initAnimation() {
-        for (PropertyValuesHolder pvHolder: mValues.values()) {
-            pvHolder.init();
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].init();
         }
         mCurrentIteration = 0;
         mInitialized = true;
     }
 
+
+    /**
+     * Sets the length of the animation.
+     *
+     * @param duration The length of the animation, in milliseconds.
+     */
+    public void setDuration(long duration) {
+        mDuration = duration;
+    }
+
+    /**
+     * Gets the length of the animation.
+     *
+     * @return The length of the animation, in milliseconds.
+     */
+    public long getDuration() {
+        return mDuration;
+    }
+
     /**
      * Sets the position of the animation to the specified point in time. This time should
      * be between 0 and the total duration of the animation, including any repetition. If
      * the animation has not yet been started, then it will not advance forward after it is
      * set to this time; it will simply set the time to this value and perform any appropriate
-     * actions based on that time. If the animation is already running, then seek() will
-     * set the current playing time to this value and continue playing from that point.
+     * actions based on that time. If the animation is already running, then setCurrentPlayTime()
+     * will set the current playing time to this value and continue playing from that point.
      *
      * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
      */
@@ -420,7 +344,7 @@
      * @return The current position in time of the animation.
      */
     public long getCurrentPlayTime() {
-        if (!mInitialized) {
+        if (!mInitialized || mPlayingState == STOPPED) {
             return 0;
         }
         return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
@@ -470,7 +394,8 @@
                         for (int i = 0; i < count; ++i) {
                             Animator anim = pendingCopy.get(i);
                             // If the animation has a startDelay, place it on the delayed list
-                            if (anim.mStartDelay == 0) {
+                            if (anim.mStartDelay == 0 || anim.mPlayingState == ENDED ||
+                                    anim.mPlayingState == CANCELED) {
                                 anim.startAnimation();
                             } else {
                                 sDelayedAnims.add(anim);
@@ -587,7 +512,11 @@
      * returns the animated value for the first of those objects.
      */
     public Object getAnimatedValue() {
-        return getAnimatedValue(mFirstPropertyName);
+        if (mValues != null && mValues.length > 0) {
+            return mValues[0].getAnimatedValue();
+        }
+        // Shouldn't get here; should always have values unless Animator was set up wrong
+        return null;
     }
 
     /**
@@ -601,7 +530,13 @@
      * by this <code>Animator</code>.
      */
     public Object getAnimatedValue(String propertyName) {
-        return mValues.get(mFirstPropertyName).getAnimatedValue();
+        PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName);
+        if (valuesHolder != null) {
+            return valuesHolder.getAnimatedValue();
+        } else {
+            // At least avoid crashing if called with bogus propertyName
+            return null;
+        }
     }
 
     /**
@@ -691,6 +626,15 @@
     }
 
     /**
+     * Returns the timing interpolator that this Animator uses.
+     *
+     * @return The timing interpolator for this Animator.
+     */
+    public Interpolator getInterpolator() {
+        return mInterpolator;
+    }
+
+    /**
      * The type evaluator to be used when calculating the animated values of this animation.
      * The system will automatically assign a float, int, or double evaluator based on the type
      * of <code>startValue</code> and <code>endValue</code> in the constructor. But if these values
@@ -707,8 +651,8 @@
      * @param value the evaluator to be used this animation
      */
     public void setEvaluator(TypeEvaluator value) {
-        if (value != null && mValues != null) {
-            mValues.get(mFirstPropertyName).setEvaluator(value);
+        if (value != null && mValues != null && mValues.length > 0) {
+            mValues[0].setEvaluator(value);
         }
     }
 
@@ -721,7 +665,12 @@
      */
     private void start(boolean playBackwards) {
         mPlayingBackwards = playBackwards;
+        if ((mStartDelay == 0) && (Thread.currentThread() == Looper.getMainLooper().getThread())) {
+            // This sets the initial value of the animation, prior to actually starting it running
+            setCurrentPlayTime(getCurrentPlayTime());
+        }
         mPlayingState = STOPPED;
+        mStartedDelay = false;
         sPendingAnimations.add(this);
         if (sAnimationHandler == null) {
             sAnimationHandler = new AnimationHandler();
@@ -752,17 +701,24 @@
 
     @Override
     public void end() {
+        if (!sAnimations.contains(this) && !sPendingAnimations.contains(this)) {
+            // Special case if the animation has not yet started; get it ready for ending
+            mStartedDelay = false;
+            sPendingAnimations.add(this);
+            if (sAnimationHandler == null) {
+                sAnimationHandler = new AnimationHandler();
+            }
+            sAnimationHandler.sendEmptyMessage(ANIMATION_START);
+        }
         // Just set the ENDED flag - this causes the animation to end the next time a frame
         // is processed.
         mPlayingState = ENDED;
     }
 
-    /**
-     * Returns whether this Animator is currently running (having been started and not yet ended).
-     * @return Wehther the Animator is running.
-     */
+    @Override
     public boolean isRunning() {
-        return mPlayingState == RUNNING;
+        // ENDED or CANCELED indicate that it has been ended or canceled, but not processed yet
+        return (mPlayingState == RUNNING || mPlayingState == ENDED || mPlayingState == CANCELED);
     }
 
     /**
@@ -790,6 +746,7 @@
      */
     private void endAnimation() {
         sAnimations.remove(this);
+        mPlayingState = STOPPED;
         if (mListeners != null) {
             ArrayList<AnimatableListener> tmpListeners =
                     (ArrayList<AnimatableListener>) mListeners.clone();
@@ -797,7 +754,6 @@
                 listener.onAnimationEnd(this);
             }
         }
-        mPlayingState = STOPPED;
     }
 
     /**
@@ -908,6 +864,7 @@
             // Fall through to set done flag
         case CANCELED:
             done = true;
+            mPlayingState = STOPPED;
             break;
         }
 
@@ -928,8 +885,9 @@
      */
     void animateValue(float fraction) {
         fraction = mInterpolator.getInterpolation(fraction);
-        for (PropertyValuesHolder valuesHolder : mValues.values()) {
-            valuesHolder.calculateValue(fraction);
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].calculateValue(fraction);
         }
         if (mUpdateListeners != null) {
             int numListeners = mUpdateListeners.size();
@@ -939,6 +897,39 @@
         }
     }
 
+    @Override
+    public Animator clone() throws CloneNotSupportedException {
+        final Animator anim = (Animator) super.clone();
+        if (mUpdateListeners != null) {
+            ArrayList<AnimatorUpdateListener> oldListeners = mUpdateListeners;
+            anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
+            int numListeners = oldListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                anim.mUpdateListeners.add(oldListeners.get(i));
+            }
+        }
+        anim.mSeekTime = -1;
+        anim.mPlayingBackwards = false;
+        anim.mCurrentIteration = 0;
+        anim.mInitialized = false;
+        anim.mPlayingState = STOPPED;
+        anim.mStartedDelay = false;
+        PropertyValuesHolder[] oldValues = mValues;
+        if (oldValues != null) {
+            int numValues = oldValues.length;
+            anim.mValues = new PropertyValuesHolder[numValues];
+            for (int i = 0; i < numValues; ++i) {
+                anim.mValues[i] = oldValues[i];
+            }
+            anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
+            for (int i = 0; i < numValues; ++i) {
+                PropertyValuesHolder valuesHolder = mValues[i];
+                anim.mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
+            }
+        }
+        return anim;
+    }
+
     /**
      * Implementors of this interface can add themselves as update listeners
      * to an <code>Animator</code> instance to receive callbacks on every animation
diff --git a/core/java/android/animation/PropertyAnimator.java b/core/java/android/animation/PropertyAnimator.java
index 9366a71..8a6edcc 100644
--- a/core/java/android/animation/PropertyAnimator.java
+++ b/core/java/android/animation/PropertyAnimator.java
@@ -16,15 +16,9 @@
 
 package android.animation;
 
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
 import android.util.Log;
 
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
  * This subclass of {@link Animator} provides support for animating properties on target objects.
@@ -61,10 +55,12 @@
      */
     public void setPropertyName(String propertyName) {
         if (mValues != null) {
-            // should always be the case
-            PropertyValuesHolder valuesHolder = mValues.get(mFirstPropertyName);
+            // mValues should always be non-null
+            PropertyValuesHolder valuesHolder = mValues[0];
+            String oldName = valuesHolder.getPropertyName();
             valuesHolder.setPropertyName(propertyName);
-            mFirstPropertyName = propertyName;
+            mValuesMap.remove(oldName);
+            mValuesMap.put(propertyName, valuesHolder);
         }
         mPropertyName = propertyName;
     }
@@ -82,25 +78,6 @@
     }
 
     /**
-     * Creates a new animation whose parameters come from the specified context and
-     * attributes set.
-     *
-     * @param context the application environment
-     * @param attrs the set of attributes holding the animation parameters
-     */
-    public PropertyAnimator(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                com.android.internal.R.styleable.PropertyAnimator);
-
-        setPropertyName(a.getString(
-                com.android.internal.R.styleable.PropertyAnimator_propertyName));
-
-
-        a.recycle();
-    }
-    /**
      * Determine the setter or getter function using the JavaBeans convention of setFoo or
      * getFoo for a property named 'foo'. This function figures out what the name of the
      * function should be and uses reflection to find the Method with that name on the
@@ -131,6 +108,14 @@
     }
 
     /**
+     * Creates a new PropertyAnimator object. This default constructor is primarily for
+     * use internally; the other constructors which take parameters are more generally
+     * useful.
+     */
+    public PropertyAnimator() {
+    }
+
+    /**
      * A constructor that takes a single property name and set of values. This constructor is
      * used in the simple case of animating a single property.
      *
@@ -184,8 +169,9 @@
     @Override
     void initAnimation() {
         super.initAnimation();
-        for (PropertyValuesHolder valuesHolder : mValues.values()) {
-            valuesHolder.setupSetterAndGetter(mTarget);
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].setupSetterAndGetter(mTarget);
         }
     }
 
@@ -223,8 +209,15 @@
     @Override
     void animateValue(float fraction) {
         super.animateValue(fraction);
-        for (PropertyValuesHolder valuesHolder : mValues.values()) {
-            valuesHolder.setAnimatedValue(mTarget);
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].setAnimatedValue(mTarget);
         }
     }
+
+    @Override
+    public PropertyAnimator clone() throws CloneNotSupportedException {
+        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 05e0bc1..fc829b8 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -261,11 +261,12 @@
                     // Swallow the error and keep trying other variants
                 }
             }
+            // If we got here, then no appropriate function was found
+            Log.e("PropertyValuesHolder",
+                    "Couldn't find setter/getter for property " + mPropertyName +
+                            "with value type "+ mValueType);
         }
-        // If we got here, then no appropriate function was found
-        Log.e("PropertyValuesHolder",
-                "Couldn't find setter/getter for property " + mPropertyName +
-                        "with value type "+ mValueType);
+
         return returnVal;
     }
 
diff --git a/core/java/android/animation/Sequencer.java b/core/java/android/animation/Sequencer.java
index 2406d8a..8779b3d 100644
--- a/core/java/android/animation/Sequencer.java
+++ b/core/java/android/animation/Sequencer.java
@@ -46,10 +46,17 @@
 public final class Sequencer extends Animatable {
 
     /**
+     * Internal variables
+     * NOTE: This object implements the clone() method, making a deep copy of any referenced
+     * objects. As other non-trivial fields are added to this class, make sure to add logic
+     * to clone() to make deep copies of them.
+     */
+
+    /**
      * Tracks animations currently being played, so that we know what to
      * cancel or end when cancel() or end() is called on this Sequencer
      */
-    private final ArrayList<Animatable> mPlayingSet = new ArrayList<Animatable>();
+    private ArrayList<Animatable> mPlayingSet = new ArrayList<Animatable>();
 
     /**
      * Contains all nodes, mapped to their respective Animatables. When new
@@ -57,21 +64,21 @@
      * to a single node representing that Animatable, not create a new Node
      * if one already exists.
      */
-    private final HashMap<Animatable, Node> mNodeMap = new HashMap<Animatable, Node>();
+    private HashMap<Animatable, Node> mNodeMap = new HashMap<Animatable, Node>();
 
     /**
      * Set of all nodes created for this Sequencer. This list is used upon
      * starting the sequencer, and the nodes are placed in sorted order into the
      * sortedNodes collection.
      */
-    private final ArrayList<Node> mNodes = new ArrayList<Node>();
+    private ArrayList<Node> mNodes = new ArrayList<Node>();
 
     /**
      * The sorted list of nodes. This is the order in which the animations will
      * be played. The details about when exactly they will be played depend
      * on the dependency relationships of the nodes.
      */
-    private final ArrayList<Node> mSortedNodes = new ArrayList<Node>();
+    private ArrayList<Node> mSortedNodes = new ArrayList<Node>();
 
     /**
      * Flag indicating whether the nodes should be sorted prior to playing. This
@@ -226,6 +233,16 @@
     @Override
     public void end() {
         mCanceled = true;
+        if (mSortedNodes.size() != mNodes.size()) {
+            // hasn't been started yet - sort the nodes now, then end them
+            sortNodes();
+            for (Node node : mSortedNodes) {
+                if (mSequenceListener == null) {
+                    mSequenceListener = new SequencerAnimatableListener(this);
+                }
+                node.animation.addListener(mSequenceListener);
+            }
+        }
         if (mSortedNodes.size() > 0) {
             for (Node node : mSortedNodes) {
                 node.animation.end();
@@ -234,6 +251,21 @@
     }
 
     /**
+     * Returns true if any of the child animations of this Sequencer have been started and have not
+     * yet ended.
+     * @return Whether this Sequencer has been started and has not yet ended.
+     */
+    @Override
+    public boolean isRunning() {
+        for (Node node : mNodes) {
+            if (node.animation.isRunning()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * {@inheritDoc}
      *
      * <p>Starting this <code>Sequencer</code> will, in turn, start the animations for which
@@ -283,6 +315,75 @@
         }
     }
 
+    @Override
+    public Sequencer clone() throws CloneNotSupportedException {
+        final Sequencer anim = (Sequencer) super.clone();
+        /*
+         * The basic clone() operation copies all items. This doesn't work very well for
+         * Sequencer, because it will copy references that need to be recreated and state
+         * that may not apply. What we need to do now is put the clone in an uninitialized
+         * state, with fresh, empty data structures. Then we will build up the nodes list
+         * manually, as we clone each Node (and its animation). The clone will then be sorted,
+         * and will populate any appropriate lists, when it is started.
+         */
+        anim.mNeedsSort = true;
+        anim.mCanceled = false;
+        anim.mPlayingSet = new ArrayList<Animatable>();
+        anim.mNodeMap = new HashMap<Animatable, Node>();
+        anim.mNodes = new ArrayList<Node>();
+        anim.mSortedNodes = new ArrayList<Node>();
+
+        // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
+        // One problem is that the old node dependencies point to nodes in the old sequencer.
+        // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
+        HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
+        for (Node node : mNodes) {
+            Node nodeClone = node.clone();
+            nodeCloneMap.put(node, nodeClone);
+            anim.mNodes.add(nodeClone);
+            anim.mNodeMap.put(nodeClone.animation, nodeClone);
+            // Clear out the dependencies in the clone; we'll set these up manually later
+            nodeClone.dependencies = null;
+            nodeClone.tmpDependencies = null;
+            nodeClone.nodeDependents = null;
+            nodeClone.nodeDependencies = null;
+            // clear out any listeners that were set up by the sequencer; these will
+            // be set up when the clone's nodes are sorted
+            ArrayList<AnimatableListener> cloneListeners = nodeClone.animation.getListeners();
+            if (cloneListeners != null) {
+                ArrayList<AnimatableListener> listenersToRemove = null;
+                for (AnimatableListener listener : cloneListeners) {
+                    if (listener instanceof SequencerAnimatableListener) {
+                        if (listenersToRemove == null) {
+                            listenersToRemove = new ArrayList<AnimatableListener>();
+                        }
+                        listenersToRemove.add(listener);
+                    }
+                }
+                if (listenersToRemove != null) {
+                    for (AnimatableListener listener : listenersToRemove) {
+                        cloneListeners.remove(listener);
+                    }
+                }
+            }
+        }
+        // Now that we've cloned all of the nodes, we're ready to walk through their
+        // dependencies, mapping the old dependencies to the new nodes
+        for (Node node : mNodes) {
+            Node nodeClone = nodeCloneMap.get(node);
+            if (node.dependencies != null) {
+                for (Dependency dependency : node.dependencies) {
+                    Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
+                    Dependency cloneDependency = new Dependency(clonedDependencyNode,
+                            dependency.rule);
+                    nodeClone.addDependency(cloneDependency);
+                }
+            }
+        }
+
+        return anim;
+    }
+
     /**
      * This class is the mechanism by which animations are started based on events in other
      * animations. If an animation has multiple dependencies on other animations, then
@@ -394,11 +495,9 @@
             Node animNode = mSequencer.mNodeMap.get(animation);
             animNode.done = true;
             ArrayList<Node> sortedNodes = mSequencer.mSortedNodes;
-            int numNodes = sortedNodes.size();
-            int nodeIndex = sortedNodes.indexOf(animNode);
             boolean allDone = true;
-            for (int i = nodeIndex + 1; i < numNodes; ++i) {
-                if (!sortedNodes.get(i).done) {
+            for (Node node : sortedNodes) {
+                if (!node.done) {
                     allDone = false;
                     break;
                 }
@@ -482,7 +581,7 @@
                         }
                     }
                 }
-                node.done = false; // also reset done flag
+                node.done = false;
             }
         }
     }
@@ -514,7 +613,7 @@
      * both dependencies upon other nodes (in the dependencies list) as
      * well as dependencies of other nodes upon this (in the nodeDependents list).
      */
-    private static class Node {
+    private static class Node implements Cloneable {
         public Animatable animation;
 
         /**
@@ -587,6 +686,13 @@
             }
             dependencyNode.nodeDependents.add(this);
         }
+
+        @Override
+        public Node clone() throws CloneNotSupportedException {
+            Node node = (Node) super.clone();
+            node.animation = (Animatable) animation.clone();
+            return node;
+        }
     }
 
     /**
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index bf660ea9..dcfc32a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -23,6 +23,7 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.CursorLoader;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -1650,7 +1651,10 @@
      * @see ContentResolver#query(android.net.Uri , String[], String, String[], String)
      * @see #startManagingCursor
      * @hide
+     *
+     * @deprecated Use {@link CursorLoader} instead.
      */
+    @Deprecated
     public final Cursor managedQuery(Uri uri, String[] projection, String selection,
             String sortOrder) {
         Cursor c = getContentResolver().query(uri, projection, selection, null, sortOrder);
@@ -1681,7 +1685,10 @@
      * 
      * @see ContentResolver#query(android.net.Uri , String[], String, String[], String)
      * @see #startManagingCursor
+     *
+     * @deprecated Use {@link CursorLoader} instead.
      */
+    @Deprecated
     public final Cursor managedQuery(Uri uri, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) {
         Cursor c = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
@@ -1707,7 +1714,10 @@
      * 
      * @see #managedQuery(android.net.Uri , String[], String, String[], String)
      * @see #stopManagingCursor
+     *
+     * @deprecated Use {@link CursorLoader} instead.
      */
+    @Deprecated
     public void startManagingCursor(Cursor c) {
         synchronized (mManagedCursors) {
             mManagedCursors.add(new ManagedCursor(c));
@@ -1722,7 +1732,10 @@
      * @param c The Cursor that was being managed.
      * 
      * @see #startManagingCursor
+     *
+     * @deprecated Use {@link CursorLoader} instead.
      */
+    @Deprecated
     public void stopManagingCursor(Cursor c) {
         synchronized (mManagedCursors) {
             final int N = mManagedCursors.size();
@@ -4087,6 +4100,7 @@
      * {@link #setImmersive}.
      *
      * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
+     * @hide
      */
     public boolean isImmersive() {
         try {
@@ -4109,6 +4123,7 @@
      *
      * @see #isImmersive
      * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
+     * @hide
      */
     public void setImmersive(boolean i) {
         try {
@@ -4281,7 +4296,14 @@
 
     final void performPause() {
         mFragments.dispatchPause();
+        mCalled = false;
         onPause();
+        if (!mCalled && getApplicationInfo().targetSdkVersion
+                >= android.os.Build.VERSION_CODES.GINGERBREAD) {
+            throw new SuperNotCalledException(
+                    "Activity " + mComponent.toShortString() +
+                    " did not call through to super.onPause()");
+        }
     }
     
     final void performUserLeaving() {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a93184d..73e8d31 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1248,8 +1248,9 @@
         
         case IS_USER_A_MONKEY_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
-            reply.writeInt(isUserAMonkey() ? 1 : 0);
+            boolean areThey = isUserAMonkey();
             reply.writeNoException();
+            reply.writeInt(areThey ? 1 : 0);
             return true;
         }
         
@@ -1263,8 +1264,9 @@
         case IS_IMMERSIVE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
-            reply.writeInt(isImmersive(token) ? 1 : 0);
+            boolean isit = isImmersive(token);
             reply.writeNoException();
+            reply.writeInt(isit ? 1 : 0);
             return true;
         }
 
@@ -1279,8 +1281,9 @@
         
         case IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
-            reply.writeInt(isTopActivityImmersive() ? 1 : 0);
+            boolean isit = isTopActivityImmersive();
             reply.writeNoException();
+            reply.writeInt(isit ? 1 : 0);
             return true;
         }
 
@@ -1294,6 +1297,40 @@
             reply.writeNoException();
             return true;
         }
+        
+        case NEW_URI_PERMISSION_OWNER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String name = data.readString();
+            IBinder perm = newUriPermissionOwner(name);
+            reply.writeNoException();
+            reply.writeStrongBinder(perm);
+            return true;
+        }
+
+        case GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder owner = data.readStrongBinder();
+            int fromUid = data.readInt();
+            String targetPkg = data.readString();
+            Uri uri = Uri.CREATOR.createFromParcel(data);
+            int mode = data.readInt();
+            grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode);
+            reply.writeNoException();
+            return true;
+        }
+
+        case REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder owner = data.readStrongBinder();
+            Uri uri = null;
+            if (data.readInt() != 0) {
+                Uri.CREATOR.createFromParcel(data);
+            }
+            int mode = data.readInt();
+            revokeUriPermissionFromOwner(owner, uri, mode);
+            reply.writeNoException();
+            return true;
+        }
 
         case DUMP_HEAP_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
@@ -2854,8 +2891,8 @@
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
         mRemote.transact(IS_IMMERSIVE_TRANSACTION, data, reply, 0);
-        boolean res = reply.readInt() == 1;
         reply.readException();
+        boolean res = reply.readInt() == 1;
         data.recycle();
         reply.recycle();
         return res;
@@ -2867,8 +2904,8 @@
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         mRemote.transact(IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION, data, reply, 0);
-        boolean res = reply.readInt() == 1;
         reply.readException();
+        boolean res = reply.readInt() == 1;
         data.recycle();
         reply.recycle();
         return res;
@@ -2889,6 +2926,55 @@
         reply.recycle();
     }
 
+    public IBinder newUriPermissionOwner(String name)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(name);
+        mRemote.transact(NEW_URI_PERMISSION_OWNER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        IBinder res = reply.readStrongBinder();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
+    public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
+            Uri uri, int mode) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(owner);
+        data.writeInt(fromUid);
+        data.writeString(targetPkg);
+        uri.writeToParcel(data, 0);
+        data.writeInt(mode);
+        mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
+            int mode) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(owner);
+        if (uri != null) {
+            data.writeInt(1);
+            uri.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
+        data.writeInt(mode);
+        mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     public boolean dumpHeap(String process, boolean managed,
             String path, ParcelFileDescriptor fd) throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -2910,6 +2996,6 @@
         data.recycle();
         return res;
     }
-
+    
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1c8c73d..4fb4bca 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3151,7 +3151,7 @@
              (ApplicationInfo.FLAG_SYSTEM |
               ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0 &&
             !"user".equals(Build.TYPE)) {
-            StrictMode.setThreadBlockingPolicy(
+            StrictMode.setThreadPolicy(
                 StrictMode.DISALLOW_DISK_WRITE |
                 StrictMode.DISALLOW_DISK_READ |
                 StrictMode.DISALLOW_NETWORK |
@@ -3343,10 +3343,6 @@
             Slog.e(TAG, "Failed to find provider info for " + name);
             return null;
         }
-        if (holder.permissionFailure != null) {
-            throw new SecurityException("Permission " + holder.permissionFailure
-                    + " required for provider " + name);
-        }
 
         IContentProvider prov = installProvider(context, holder.provider,
                 holder.info, true);
diff --git a/core/java/android/app/BackStackEntry.java b/core/java/android/app/BackStackEntry.java
index 520e4fd..00c2fc4 100644
--- a/core/java/android/app/BackStackEntry.java
+++ b/core/java/android/app/BackStackEntry.java
@@ -410,17 +410,13 @@
         
         mManager.moveToState(mManager.mCurState, mTransition,
                 mTransitionStyle, true);
-        if (mManager.mNeedMenuInvalidate && mManager.mActivity != null) {
-            mManager.mActivity.invalidateOptionsMenu();
-            mManager.mNeedMenuInvalidate = false;
-        }
         
         if (mAddToBackStack) {
             mManager.addBackStackState(this);
         }
     }
     
-    public void popFromBackStack() {
+    public void popFromBackStack(boolean doStateMove) {
         if (FragmentManagerImpl.DEBUG) Log.v(TAG, "popFromBackStack: " + this);
 
         bumpBackStackNesting(-1);
@@ -472,11 +468,9 @@
             op = op.prev;
         }
         
-        mManager.moveToState(mManager.mCurState,
-                FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
-        if (mManager.mNeedMenuInvalidate && mManager.mActivity != null) {
-            mManager.mActivity.invalidateOptionsMenu();
-            mManager.mNeedMenuInvalidate = false;
+        if (doStateMove) {
+            mManager.moveToState(mManager.mCurState,
+                    FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
         }
 
         if (mIndex >= 0) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e34bad2..fee95aa 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2878,6 +2878,11 @@
                 }
             }
 
+            public void startCommit() {
+                // TODO: implement
+                commit();
+            }
+
             public boolean commit() {
                 boolean returnValue;
 
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index f780e1d..8ba480d 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -34,6 +34,9 @@
 
 /**
  * A simple dialog containing an {@link android.widget.DatePicker}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
  */
 public class DatePickerDialog extends AlertDialog implements OnClickListener,
         OnDateChangedListener {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 274a266..a0be0cd 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -330,7 +330,7 @@
     }
 
     /**
-     * Similar to {@link Activity#onCreate}, you should initialized your dialog
+     * Similar to {@link Activity#onCreate}, you should initialize your dialog
      * in this method, including calling {@link #setContentView}.
      * @param savedInstanceState If this dialog is being reinitalized after a
      *     the hosting activity was previously shut down, holds the result from
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index 50e7421..e8dfac9 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -36,6 +36,102 @@
  * content of the dialog.  Alternatively, they can override
  * {@link #onCreateDialog(Bundle)} to create an entirely custom dialog, such
  * as an AlertDialog, with its own content.
+ *
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#Lifecycle">Lifecycle</a>
+ * <li><a href="#BasicDialog">Basic Dialog</a>
+ * <li><a href="#AlertDialog">Alert Dialog</a>
+ * <li><a href="#DialogOrEmbed">Selecting Between Dialog or Embedding</a>
+ * </ol>
+ *
+ * <a name="Lifecycle"></a>
+ * <h3>Lifecycle</h3>
+ *
+ * <p>DialogFragment does various things to keep the fragment's lifecycle
+ * driving it, instead of the Dialog.  Note that dialogs are generally
+ * autonomous entities -- they are their own window, receiving their own
+ * input events, and often deciding on their own when to disappear (by
+ * receiving a back key event or the user clicking on a button).
+ *
+ * <p>DialogFragment needs to ensure that what is happening with the Fragment
+ * and Dialog states remains consistent.  To do this, it watches for dismiss
+ * events from the dialog and takes are of removing its own state when they
+ * happen.  This means you should use {@link #show(FragmentManager, String)}
+ * or {@link #show(FragmentTransaction, String)} to add an instance of
+ * DialogFragment to your UI, as these keep track of how DialogFragment should
+ * remove itself when the dialog is dismissed.
+ *
+ * <a name="BasicDialog"></a>
+ * <h3>Basic Dialog</h3>
+ *
+ * <p>The simplest use of DialogFragment is as a floating container for the
+ * fragment's view hierarchy.  A simple implementation may look like this:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialog.java
+ *      dialog}
+ *
+ * <p>An example showDialog() method on the Activity could be:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialog.java
+ *      add_dialog}
+ *
+ * <p>This removes any currently shown dialog, creates a new DialogFragment
+ * with an argument, and shows it as a new state on the back stack.  When the
+ * transaction is popped, the current DialogFragment and its Dialog will be
+ * destroyed, and the previous one (if any) re-shown.  Note that in this case
+ * DialogFragment will take care of popping the transaction of the Dialog
+ * is dismissed separately from it.
+ *
+ * <a name="AlertDialog"></a>
+ * <h3>Alert Dialog</h3>
+ *
+ * <p>Instead of (or in addition to) implementing {@link #onCreateView} to
+ * generate the view hierarchy inside of a dialog, you may implement
+ * {@link #onCreateDialog(Bundle)} to create your own custom Dialog object.
+ *
+ * <p>This is most useful for creating an {@link AlertDialog}, allowing you
+ * to display standard alerts to the user that are managed by a fragment.
+ * A simple example implementation of this is:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialog.java
+ *      dialog}
+ *
+ * <p>The activity creating this fragment may have the following methods to
+ * show the dialog and receive results from it:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialog.java
+ *      activity}
+ *
+ * <p>Note that in this case the fragment is not placed on the back stack, it
+ * is just added as an indefinitely running fragment.  Because dialogs normally
+ * are modal, this will still operate as a back stack, since the dialog will
+ * capture user input until it is dismissed.  When it is dismissed, DialogFragment
+ * will take care of removing itself from its fragment manager.
+ *
+ * <a name="DialogOrEmbed"></a>
+ * <h3>Selecting Between Dialog or Embedding</h3>
+ *
+ * <p>A DialogFragment can still optionally be used as a normal fragment, if
+ * desired.  This is useful if you have a fragment that in some cases should
+ * be shown as a dialog and others embedded in a larger UI.  This behavior
+ * will normally be automatically selected for you based on how you are using
+ * the fragment, but can be customized with {@link #setShowsDialog(boolean)}.
+ *
+ * <p>For example, here is a simple dialog fragment:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ *      dialog}
+ *
+ * <p>An instance of this fragment can be created and shown as a dialog:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ *      show_dialog}
+ *
+ * <p>It can also be added as content in a view hierarchy:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ *      embed}
  */
 public class DialogFragment extends Fragment
         implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
@@ -70,11 +166,13 @@
     private static final String SAVED_STYLE = "android:style";
     private static final String SAVED_THEME = "android:theme";
     private static final String SAVED_CANCELABLE = "android:cancelable";
+    private static final String SAVED_SHOWS_DIALOG = "android:showsDialog";
     private static final String SAVED_BACK_STACK_ID = "android:backStackId";
 
     int mStyle = STYLE_NORMAL;
     int mTheme = 0;
     boolean mCancelable = true;
+    boolean mShowsDialog = true;
     int mBackStackId = -1;
 
     Dialog mDialog;
@@ -109,16 +207,9 @@
     }
 
     /**
-     * Display the dialog, adding the fragment to the given activity.  This
-     * is a convenience for explicitly creating a transaction, adding the
-     * fragment to it with the given tag, and committing it.  This does
-     * <em>not</em> add the transaction to the back stack.  When the fragment
-     * is dismissed, a new transaction will be executed to remove it from
-     * the activity.
-     * @param activity The activity this fragment will be added to.
-     * @param tag The tag for this fragment, as per
-     * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+     * @deprecated Please use {@link #show(FragmentManager, String)}.
      */
+    @Deprecated
     public void show(Activity activity, String tag) {
         FragmentTransaction ft = activity.openFragmentTransaction();
         ft.add(this, tag);
@@ -126,16 +217,32 @@
     }
 
     /**
-     * Display the dialog, adding the fragment to the given activity using
-     * an existing transaction and then committing the transaction.
-     * @param activity The activity this fragment will be added to.
+     * Display the dialog, adding the fragment to the given FragmentManager.  This
+     * is a convenience for explicitly creating a transaction, adding the
+     * fragment to it with the given tag, and committing it.  This does
+     * <em>not</em> add the transaction to the back stack.  When the fragment
+     * is dismissed, a new transaction will be executed to remove it from
+     * the activity.
+     * @param manager The FragmentManager this fragment will be added to.
+     * @param tag The tag for this fragment, as per
+     * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+     */
+    public void show(FragmentManager manager, String tag) {
+        FragmentTransaction ft = manager.openTransaction();
+        ft.add(this, tag);
+        ft.commit();
+    }
+
+    /**
+     * Display the dialog, adding the fragment using an existing transaction
+     * and then committing the transaction.
      * @param transaction An existing transaction in which to add the fragment.
      * @param tag The tag for this fragment, as per
      * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
      * @return Returns the identifier of the committed transaction, as per
      * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.
      */
-    public int show(Activity activity, FragmentTransaction transaction, String tag) {
+    public int show(FragmentTransaction transaction, String tag) {
         transaction.add(this, tag);
         mRemoved = false;
         mBackStackId = transaction.commit();
@@ -173,23 +280,67 @@
         return mTheme;
     }
 
+    /**
+     * Control whether the shown Dialog is cancelable.  Use this instead of
+     * directly calling {@link Dialog#setCancelable(boolean)
+     * Dialog.setCancelable(boolean)}, because DialogFragment needs to change
+     * its behavior based on this.
+     *
+     * @param cancelable If true, the dialog is cancelable.  The default
+     * is true.
+     */
     public void setCancelable(boolean cancelable) {
         mCancelable = cancelable;
         if (mDialog != null) mDialog.setCancelable(cancelable);
     }
 
+    /**
+     * Return the current value of {@link #setCancelable(boolean)}.
+     */
     public boolean getCancelable() {
         return mCancelable;
     }
 
+    /**
+     * Controls whether this fragment should be shown in a dialog.  If not
+     * set, no Dialog will be created in {@link #onActivityCreated(Bundle)},
+     * and the fragment's view hierarchy will thus not be added to it.  This
+     * allows you to instead use it as a normal fragment (embedded inside of
+     * its activity).
+     *
+     * <p>This is normally set for you based on whether the fragment is
+     * associated with a container view ID passed to
+     * {@link FragmentTransaction#add(int, Fragment) FragmentTransaction.add(int, Fragment)}.
+     * If the fragment was added with a container, setShowsDialog will be
+     * initialized to false; otherwise, it will be true.
+     *
+     * @param showsDialog If true, the fragment will be displayed in a Dialog.
+     * If false, no Dialog will be created and the fragment's view hierarchly
+     * left undisturbed.
+     */
+    public void setShowsDialog(boolean showsDialog) {
+        mShowsDialog = showsDialog;
+    }
+
+    /**
+     * Return the current value of {@link #setShowsDialog(boolean)}.
+     */
+    public boolean getShowsDialog() {
+        return mShowsDialog;
+    }
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        mShowsDialog = mContainerId == 0;
+
         if (savedInstanceState != null) {
-            mStyle = savedInstanceState.getInt(SAVED_STYLE, mStyle);
-            mTheme = savedInstanceState.getInt(SAVED_THEME, mTheme);
-            mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, mCancelable);
-            mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, mBackStackId);
+            mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL);
+            mTheme = savedInstanceState.getInt(SAVED_THEME, 0);
+            mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
+            mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
+            mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
         }
     }
 
@@ -209,6 +360,11 @@
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
+
+        if (!mShowsDialog) {
+            return;
+        }
+
         mDialog = onCreateDialog(savedInstanceState);
         mDestroyed = false;
         switch (mStyle) {
@@ -258,10 +414,21 @@
                 outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
             }
         }
-        outState.putInt(SAVED_STYLE, mStyle);
-        outState.putInt(SAVED_THEME, mTheme);
-        outState.putBoolean(SAVED_CANCELABLE, mCancelable);
-        outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
+        if (mStyle != STYLE_NORMAL) {
+            outState.putInt(SAVED_STYLE, mStyle);
+        }
+        if (mTheme != 0) {
+            outState.putInt(SAVED_THEME, mTheme);
+        }
+        if (!mCancelable) {
+            outState.putBoolean(SAVED_CANCELABLE, mCancelable);
+        }
+        if (!mShowsDialog) {
+            outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
+        }
+        if (mBackStackId != -1) {
+            outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
+        }
     }
 
     @Override
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index b10a8a8..56cf399 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -849,6 +849,25 @@
         mCalled = true;
     }
     
+    /**
+     * Called to ask the fragment to save its current dynamic state, so it
+     * can later be reconstructed in a new instance of its process is
+     * restarted.  If a new instance of the fragment later needs to be
+     * created, the data you place in the Bundle here will be available
+     * in the Bundle given to {@link #onCreate(Bundle)},
+     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}, and
+     * {@link #onActivityCreated(Bundle)}.
+     *
+     * <p>This corresponds to {@link Activity#onSaveInstanceState(Bundle)
+     * Activity.onnSaveInstanceState(Bundle)} and most of the discussion there
+     * applies here as well.  Note however: <em>this method may be called
+     * at any time before {@link #onDestroy()}</em>.  There are many situations
+     * where a fragment may be mostly torn down (such as when placed on the
+     * back stack with no UI showing), but its state will not be saved until
+     * its owning activity actually needs to save its state.
+     *
+     * @param outState Bundle in which to place your saved state.
+     */
     public void onSaveInstanceState(Bundle outState) {
     }
     
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index f190d0d..7641f61 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.animation.Animatable;
+import android.animation.AnimatableInflater;
 import android.animation.PropertyAnimator;
 import android.animation.Sequencer;
 import android.content.res.TypedArray;
@@ -31,7 +32,6 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
 
 import java.util.ArrayList;
 
@@ -257,7 +257,7 @@
         }
         
         if (fragment.mNextAnim != 0) {
-            Animatable anim = AnimationUtils.loadAnimator(mActivity, fragment.mNextAnim);
+            Animatable anim = AnimatableInflater.loadAnimatable(mActivity, fragment.mNextAnim);
             if (anim != null) {
                 return anim;
             }
@@ -288,7 +288,7 @@
             return null;
         }
         
-        return AnimationUtils.loadAnimator(mActivity, anim);
+        return AnimatableInflater.loadAnimatable(mActivity, anim);
     }
     
     void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
@@ -511,6 +511,11 @@
                     moveToState(f, newState, transit, transitStyle);
                 }
             }
+
+            if (mNeedMenuInvalidate && mActivity != null) {
+                mActivity.invalidateOptionsMenu();
+                mNeedMenuInvalidate = false;
+            }
         }
     }
     
@@ -800,9 +805,7 @@
             enqueueAction(new Runnable() {
                 public void run() {
                     if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss);
-                    bss.popFromBackStack();
-                    moveToState(mCurState, reverseTransit(bss.getTransition()),
-                            bss.getTransitionStyle(), true);
+                    bss.popFromBackStack(true);
                 }
             });
         } else {
@@ -848,11 +851,11 @@
             }
             enqueueAction(new Runnable() {
                 public void run() {
-                    for (int i=0; i<states.size(); i++) {
+                    final int LAST = states.size()-1;
+                    for (int i=0; i<=LAST; i++) {
                         if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
-                        states.get(i).popFromBackStack();
+                        states.get(i).popFromBackStack(i == LAST);
                     }
-                    moveToState(mCurState, true);
                 }
             });
         }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 0d35ba4..901f117 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -317,6 +317,12 @@
     
     public void crashApplication(int uid, int initialPid, String packageName,
             String message) throws RemoteException;
+    
+    public IBinder newUriPermissionOwner(String name) throws RemoteException;
+    public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
+            Uri uri, int mode) throws RemoteException;
+    public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
+            int mode) throws RemoteException;
 
     // Cause the specified process to dump the specified heap.
     public boolean dumpHeap(String process, boolean managed, String path,
@@ -330,28 +336,19 @@
     /** Information you can retrieve about a particular application. */
     public static class ContentProviderHolder implements Parcelable {
         public final ProviderInfo info;
-        public final String permissionFailure;
         public IContentProvider provider;
         public boolean noReleaseNeeded;
 
         public ContentProviderHolder(ProviderInfo _info) {
             info = _info;
-            permissionFailure = null;
         }
 
-        public ContentProviderHolder(ProviderInfo _info,
-                String _permissionFailure) {
-            info = _info;
-            permissionFailure = _permissionFailure;
-        }
-        
         public int describeContents() {
             return 0;
         }
 
         public void writeToParcel(Parcel dest, int flags) {
             info.writeToParcel(dest, 0);
-            dest.writeString(permissionFailure);
             if (provider != null) {
                 dest.writeStrongBinder(provider.asBinder());
             } else {
@@ -373,7 +370,6 @@
 
         private ContentProviderHolder(Parcel source) {
             info = ProviderInfo.CREATOR.createFromParcel(source);
-            permissionFailure = source.readString();
             provider = ContentProviderNative.asInterface(
                 source.readStrongBinder());
             noReleaseNeeded = source.readInt() != 0;
@@ -538,5 +534,8 @@
     int SET_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+111;
     int IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+112;
     int CRASH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+113;
-    int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
+    int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
+    int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115;
+    int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116;
+    int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
 }
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index a04b9e9..521d41c 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -32,6 +32,9 @@
 
 /**
  * A dialog that prompts the user for the time of day using a {@link TimePicker}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker
+ * tutorial</a>.</p>
  */
 public class TimePickerDialog extends AlertDialog implements OnClickListener,
         OnTimeChangedListener {
diff --git a/core/java/android/bluetooth/AtCommandHandler.java b/core/java/android/bluetooth/AtCommandHandler.java
index 8de2133..6deab34 100644
--- a/core/java/android/bluetooth/AtCommandHandler.java
+++ b/core/java/android/bluetooth/AtCommandHandler.java
@@ -73,7 +73,7 @@
      *             least one element in this array.
      * @return     The result of this command.
      */
-    // Typically used to set this paramter
+    // Typically used to set this parameter
     public AtCommandResult handleSetCommand(Object[] args) {
         return new AtCommandResult(AtCommandResult.ERROR);
     }
@@ -83,11 +83,12 @@
      * Test commands are part of the Extended command syntax, and are typically
      * used to request an indication of the range of legal values that "FOO"
      * can take.<p>
-     * By defualt we return an OK result, to indicate that this command is at
+     * By default we return an OK result, to indicate that this command is at
      * least recognized.<p>
      * @return The result of this command.
      */
     public AtCommandResult handleTestCommand() {
         return new AtCommandResult(AtCommandResult.OK);
     }
+
 }
diff --git a/core/java/android/bluetooth/BluetoothAssignedNumbers.java b/core/java/android/bluetooth/BluetoothAssignedNumbers.java
new file mode 100644
index 0000000..55bc814
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAssignedNumbers.java
@@ -0,0 +1,523 @@
+/*
+ * 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;
+
+/**
+ * Bluetooth Assigned Numbers.
+ * <p>
+ * For now we only include Company ID values.
+ * @see <a href="https://www.bluetooth.org/technical/assignednumbers/identifiers.htm">
+ * The Official Bluetooth SIG Member Website | Company Identifiers</a>
+ *
+ * @hide
+ */
+public class BluetoothAssignedNumbers {
+
+    //// Bluetooth SIG Company ID values
+
+    /*
+     * Ericsson Technology Licensing.
+     */
+    public static final int ERICSSON_TECHNOLOGY = 0x0000;
+
+    /*
+     * Nokia Mobile Phones.
+     */
+    public static final int NOKIA_MOBILE_PHONES = 0x0001;
+
+    /*
+     * Intel Corp.
+     */
+    public static final int INTEL = 0x0002;
+
+    /*
+     * IBM Corp.
+     */
+    public static final int IBM = 0x0003;
+
+    /*
+     * Toshiba Corp.
+     */
+    public static final int TOSHIBA = 0x0004;
+
+    /*
+     * 3Com.
+     */
+    public static final int THREECOM = 0x0005;
+
+    /*
+     * Microsoft.
+     */
+    public static final int MICROSOFT = 0x0006;
+
+    /*
+     * Lucent.
+     */
+    public static final int LUCENT = 0x0007;
+
+    /*
+     * Motorola.
+     */
+    public static final int MOTOROLA = 0x0008;
+
+    /*
+     * Infineon Technologies AG.
+     */
+    public static final int INFINEON_TECHNOLOGIES = 0x0009;
+
+    /*
+     * Cambridge Silicon Radio.
+     */
+    public static final int CAMBRIDGE_SILICON_RADIO = 0x000A;
+
+    /*
+     * Silicon Wave.
+     */
+    public static final int SILICON_WAVE = 0x000B;
+
+    /*
+     * Digianswer A/S.
+     */
+    public static final int DIGIANSWER = 0x000C;
+
+    /*
+     * Texas Instruments Inc.
+     */
+    public static final int TEXAS_INSTRUMENTS = 0x000D;
+
+    /*
+     * Parthus Technologies Inc.
+     */
+    public static final int PARTHUS_TECHNOLOGIES = 0x000E;
+
+    /*
+     * Broadcom Corporation.
+     */
+    public static final int BROADCOM = 0x000F;
+
+    /*
+     * Mitel Semiconductor.
+     */
+    public static final int MITEL_SEMICONDUCTOR = 0x0010;
+
+    /*
+     * Widcomm, Inc.
+     */
+    public static final int WIDCOMM = 0x0011;
+
+    /*
+     * Zeevo, Inc.
+     */
+    public static final int ZEEVO = 0x0012;
+
+    /*
+     * Atmel Corporation.
+     */
+    public static final int ATMEL = 0x0013;
+
+    /*
+     * Mitsubishi Electric Corporation.
+     */
+    public static final int MITSUBISHI_ELECTRIC = 0x0014;
+
+    /*
+     * RTX Telecom A/S.
+     */
+    public static final int RTX_TELECOM = 0x0015;
+
+    /*
+     * KC Technology Inc.
+     */
+    public static final int KC_TECHNOLOGY = 0x0016;
+
+    /*
+     * Newlogic.
+     */
+    public static final int NEWLOGIC = 0x0017;
+
+    /*
+     * Transilica, Inc.
+     */
+    public static final int TRANSILICA = 0x0018;
+
+    /*
+     * Rohde & Schwarz GmbH & Co. KG.
+     */
+    public static final int ROHDE_AND_SCHWARZ = 0x0019;
+
+    /*
+     * TTPCom Limited.
+     */
+    public static final int TTPCOM = 0x001A;
+
+    /*
+     * Signia Technologies, Inc.
+     */
+    public static final int SIGNIA_TECHNOLOGIES = 0x001B;
+
+    /*
+     * Conexant Systems Inc.
+     */
+    public static final int CONEXANT_SYSTEMS = 0x001C;
+
+    /*
+     * Qualcomm.
+     */
+    public static final int QUALCOMM = 0x001D;
+
+    /*
+     * Inventel.
+     */
+    public static final int INVENTEL = 0x001E;
+
+    /*
+     * AVM Berlin.
+     */
+    public static final int AVM_BERLIN = 0x001F;
+
+    /*
+     * BandSpeed, Inc.
+     */
+    public static final int BANDSPEED = 0x0020;
+
+    /*
+     * Mansella Ltd.
+     */
+    public static final int MANSELLA = 0x0021;
+
+    /*
+     * NEC Corporation.
+     */
+    public static final int NEC = 0x0022;
+
+    /*
+     * WavePlus Technology Co., Ltd.
+     */
+    public static final int WAVEPLUS_TECHNOLOGY = 0x0023;
+
+    /*
+     * Alcatel.
+     */
+    public static final int ALCATEL = 0x0024;
+
+    /*
+     * Philips Semiconductors.
+     */
+    public static final int PHILIPS_SEMICONDUCTORS = 0x0025;
+
+    /*
+     * C Technologies.
+     */
+    public static final int C_TECHNOLOGIES = 0x0026;
+
+    /*
+     * Open Interface.
+     */
+    public static final int OPEN_INTERFACE = 0x0027;
+
+    /*
+     * R F Micro Devices.
+     */
+    public static final int RF_MICRO_DEVICES = 0x0028;
+
+    /*
+     * Hitachi Ltd.
+     */
+    public static final int HITACHI = 0x0029;
+
+    /*
+     * Symbol Technologies, Inc.
+     */
+    public static final int SYMBOL_TECHNOLOGIES = 0x002A;
+
+    /*
+     * Tenovis.
+     */
+    public static final int TENOVIS = 0x002B;
+
+    /*
+     * Macronix International Co. Ltd.
+     */
+    public static final int MACRONIX = 0x002C;
+
+    /*
+     * GCT Semiconductor.
+     */
+    public static final int GCT_SEMICONDUCTOR = 0x002D;
+
+    /*
+     * Norwood Systems.
+     */
+    public static final int NORWOOD_SYSTEMS = 0x002E;
+
+    /*
+     * MewTel Technology Inc.
+     */
+    public static final int MEWTEL_TECHNOLOGY = 0x002F;
+
+    /*
+     * ST Microelectronics.
+     */
+    public static final int ST_MICROELECTRONICS = 0x0030;
+
+    /*
+     * Synopsys.
+     */
+    public static final int SYNOPSYS = 0x0031;
+
+    /*
+     * Red-M (Communications) Ltd.
+     */
+    public static final int RED_M = 0x0032;
+
+    /*
+     * Commil Ltd.
+     */
+    public static final int COMMIL = 0x0033;
+
+    /*
+     * Computer Access Technology Corporation (CATC).
+     */
+    public static final int CATC = 0x0034;
+
+    /*
+     * Eclipse (HQ Espana) S.L.
+     */
+    public static final int ECLIPSE = 0x0035;
+
+    /*
+     * Renesas Technology Corp.
+     */
+    public static final int RENESAS_TECHNOLOGY = 0x0036;
+
+    /*
+     * Mobilian Corporation.
+     */
+    public static final int MOBILIAN_CORPORATION = 0x0037;
+
+    /*
+     * Terax.
+     */
+    public static final int TERAX = 0x0038;
+
+    /*
+     * Integrated System Solution Corp.
+     */
+    public static final int INTEGRATED_SYSTEM_SOLUTION = 0x0039;
+
+    /*
+     * Matsushita Electric Industrial Co., Ltd.
+     */
+    public static final int MATSUSHITA_ELECTRIC = 0x003A;
+
+    /*
+     * Gennum Corporation.
+     */
+    public static final int GENNUM = 0x003B;
+
+    /*
+     * Research In Motion.
+     */
+    public static final int RESEARCH_IN_MOTION = 0x003C;
+
+    /*
+     * IPextreme, Inc.
+     */
+    public static final int IPEXTREME = 0x003D;
+
+    /*
+     * Systems and Chips, Inc.
+     */
+    public static final int SYSTEMS_AND_CHIPS = 0x003E;
+
+    /*
+     * Bluetooth SIG, Inc.
+     */
+    public static final int BLUETOOTH_SIG = 0x003F;
+
+    /*
+     * Seiko Epson Corporation.
+     */
+    public static final int SEIKO_EPSON = 0x0040;
+
+    /*
+     * Integrated Silicon Solution Taiwan, Inc.
+     */
+    public static final int INTEGRATED_SILICON_SOLUTION = 0x0041;
+
+    /*
+     * CONWISE Technology Corporation Ltd.
+     */
+    public static final int CONWISE_TECHNOLOGY = 0x0042;
+
+    /*
+     * PARROT SA.
+     */
+    public static final int PARROT = 0x0043;
+
+    /*
+     * Socket Mobile.
+     */
+    public static final int SOCKET_MOBILE = 0x0044;
+
+    /*
+     * Atheros Communications, Inc.
+     */
+    public static final int ATHEROS_COMMUNICATIONS = 0x0045;
+
+    /*
+     * MediaTek, Inc.
+     */
+    public static final int MEDIATEK = 0x0046;
+
+    /*
+     * Bluegiga.
+     */
+    public static final int BLUEGIGA = 0x0047;
+
+    /*
+     * Marvell Technology Group Ltd.
+     */
+    public static final int MARVELL = 0x0048;
+
+    /*
+     * 3DSP Corporation.
+     */
+    public static final int THREE_DSP = 0x0049;
+
+    /*
+     * Accel Semiconductor Ltd.
+     */
+    public static final int ACCEL_SEMICONDUCTOR = 0x004A;
+
+    /*
+     * Continental Automotive Systems.
+     */
+    public static final int CONTINENTAL_AUTOMOTIVE = 0x004B;
+
+    /*
+     * Apple, Inc.
+     */
+    public static final int APPLE = 0x004C;
+
+    /*
+     * Staccato Communications, Inc.
+     */
+    public static final int STACCATO_COMMUNICATIONS = 0x004D;
+
+    /*
+     * Avago Technologies.
+     */
+    public static final int AVAGO = 0x004E;
+
+    /*
+     * APT Licensing Ltd.
+     */
+    public static final int APT_LICENSING = 0x004F;
+
+    /*
+     * SiRF Technology, Inc.
+     */
+    public static final int SIRF_TECHNOLOGY = 0x0050;
+
+    /*
+     * Tzero Technologies, Inc.
+     */
+    public static final int TZERO_TECHNOLOGIES = 0x0051;
+
+    /*
+     * J&M Corporation.
+     */
+    public static final int J_AND_M = 0x0052;
+
+    /*
+     * Free2move AB.
+     */
+    public static final int FREE2MOVE = 0x0053;
+
+    /*
+     * 3DiJoy Corporation.
+     */
+    public static final int THREE_DIJOY = 0x0054;
+
+    /*
+     * Plantronics, Inc.
+     */
+    public static final int PLANTRONICS = 0x0055;
+
+    /*
+     * Sony Ericsson Mobile Communications.
+     */
+    public static final int SONY_ERICSSON = 0x0056;
+
+    /*
+     * Harman International Industries, Inc.
+     */
+    public static final int HARMAN_INTERNATIONAL = 0x0057;
+
+    /*
+     * Vizio, Inc.
+     */
+    public static final int VIZIO = 0x0058;
+
+    /*
+     * Nordic Semiconductor ASA.
+     */
+    public static final int NORDIC_SEMICONDUCTOR = 0x0059;
+
+    /*
+     * EM Microelectronic-Marin SA.
+     */
+    public static final int EM_MICROELECTRONIC_MARIN = 0x005A;
+
+    /*
+     * Ralink Technology Corporation.
+     */
+    public static final int RALINK_TECHNOLOGY = 0x005B;
+
+    /*
+     * Belkin International, Inc.
+     */
+    public static final int BELKIN_INTERNATIONAL = 0x005C;
+
+    /*
+     * Realtek Semiconductor Corporation.
+     */
+    public static final int REALTEK_SEMICONDUCTOR = 0x005D;
+
+    /*
+     * Stonestreet One, LLC.
+     */
+    public static final int STONESTREET_ONE = 0x005E;
+
+    /*
+     * Wicentric, Inc.
+     */
+    public static final int WICENTRIC = 0x005F;
+
+    /*
+     * RivieraWaves S.A.S.
+     */
+    public static final int RIVIERAWAVES = 0x0060;
+
+    /*
+     * You can't instantiate one of these.
+     */
+    private BluetoothAssignedNumbers() {
+    }
+
+}
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 0c9bab2..c8381c9 100644
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -261,6 +261,10 @@
     public static final int PROFILE_OPP = 2;
     /** @hide */
     public static final int PROFILE_HID = 3;
+    /** @hide */
+    public static final int PROFILE_PANU = 4;
+    /** @hide */
+    public static final int PROFILE_NAP = 5;
 
     /**
      * Check class bits for possible bluetooth profile support.
@@ -328,6 +332,12 @@
             }
         } else if (profile == PROFILE_HID) {
             return (getDeviceClass() & Device.Major.PERIPHERAL) == Device.Major.PERIPHERAL;
+        } else if (profile == PROFILE_PANU || profile == PROFILE_NAP){
+            // No good way to distinguish between the two, based on class bits.
+            if (hasService(Service.NETWORKING)) {
+                return true;
+            }
+            return (getDeviceClass() & Device.Major.NETWORKING) == Device.Major.NETWORKING;
         } else {
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 4a91a8c..be21d46 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -45,7 +45,7 @@
  * This BluetoothHeadset object is not immediately bound to the
  * BluetoothHeadset service. Use the ServiceListener interface to obtain a
  * notification when it is bound, this is especially important if you wish to
- * immediately call methods on BluetootHeadset after construction.
+ * immediately call methods on BluetoothHeadset after construction.
  *
  * Android only supports one connected Bluetooth Headset at a time.
  *
@@ -85,6 +85,47 @@
             "android.bluetooth.headset.extra.DISCONNECT_INITIATOR";
 
     /**
+     * Broadcast Action: Indicates a headset has posted a vendor-specific event.
+     * <p>Always contains the extra fields {@link #EXTRA_DEVICE},
+     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD}, and
+     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS}.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
+            "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
+
+    /**
+     * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
+     * intents that contains the name of the vendor-specific command.
+     * @hide
+     */
+    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
+            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
+
+    /**
+     * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
+     * intents that contains the Company ID of the vendor defining the vendor-specific
+     * command.
+     * @see <a href="https://www.bluetooth.org/Technical/AssignedNumbers/identifiers.htm">
+     * Bluetooth SIG Assigned Numbers - Company Identifiers</a>
+     * @hide
+     */
+    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID =
+            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID";
+
+    /**
+     * A Parcelable String array extra field in
+     * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
+     * the arguments to the vendor-specific command.
+     * @hide
+     */
+    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
+            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
+
+
+    /**
      * TODO(API release): Consider incorporating as new state in
      * HEADSET_STATE_CHANGED
      */
@@ -108,7 +149,7 @@
 
     public static final int RESULT_FAILURE = 0;
     public static final int RESULT_SUCCESS = 1;
-    /** Connection canceled before completetion. */
+    /** Connection canceled before completion. */
     public static final int RESULT_CANCELED = 2;
 
     /** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index fb3dfe4..3fbfc70 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -55,6 +55,8 @@
             ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid NAP =
             ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
+    public static final ParcelUuid BNEP =
+            ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB");
 
     public static final ParcelUuid[] RESERVED_UUIDS = {
         AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
@@ -92,13 +94,17 @@
         return uuid.equals(Hid);
     }
 
-    public static boolean isPANU(ParcelUuid uuid) {
+    public static boolean isPanu(ParcelUuid uuid) {
         return uuid.equals(PANU);
     }
 
-    public static boolean isNAP(ParcelUuid uuid) {
+    public static boolean isNap(ParcelUuid uuid) {
         return uuid.equals(NAP);
     }
+
+    public static boolean isBnep(ParcelUuid uuid) {
+        return uuid.equals(BNEP);
+    }
     /**
      * Returns true if ParcelUuid is present in uuidArray
      *
diff --git a/core/java/android/bluetooth/HeadsetBase.java b/core/java/android/bluetooth/HeadsetBase.java
index e2935c9..9ef2eb5 100644
--- a/core/java/android/bluetooth/HeadsetBase.java
+++ b/core/java/android/bluetooth/HeadsetBase.java
@@ -74,8 +74,8 @@
 
     private native void cleanupNativeDataNative();
 
-    public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device,
-            int rfcommChannel) {
+    public HeadsetBase(PowerManager pm, BluetoothAdapter adapter,
+                       BluetoothDevice device, int rfcommChannel) {
         mDirection = DIRECTION_OUTGOING;
         mConnectTimestamp = System.currentTimeMillis();
         mAdapter = adapter;
@@ -89,9 +89,10 @@
         initializeNativeDataNative(-1);
     }
 
-    /* Create from an already exisiting rfcomm connection */
-    public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device,
-            int socketFd, int rfcommChannel, Handler handler) {
+    /* Create from an existing rfcomm connection */
+    public HeadsetBase(PowerManager pm, BluetoothAdapter adapter,
+                       BluetoothDevice device,
+                       int socketFd, int rfcommChannel, Handler handler) {
         mDirection = DIRECTION_INCOMING;
         mConnectTimestamp = System.currentTimeMillis();
         mAdapter = adapter;
@@ -128,7 +129,7 @@
                        (System.currentTimeMillis() - timestamp) + " ms");
 
         if (result.getResultCode() == AtCommandResult.ERROR) {
-            Log.i(TAG, "Error pocessing <" + input + ">");
+            Log.i(TAG, "Error processing <" + input + ">");
         }
 
         sendURC(result.toString());
@@ -142,8 +143,9 @@
      */
     protected void initializeAtParser() {
         mAtParser = new AtParser();
-        //TODO(): Get rid of this as there are no parsers registered. But because of dependencies,
-        //it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree
+
+        //TODO(): Get rid of this as there are no parsers registered. But because of dependencies
+        // it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree
     }
 
     public AtParser getAtParser() {
@@ -159,8 +161,7 @@
                         String input = readNative(500);
                         if (input != null) {
                             handleInput(input);
-                        }
-                        else {
+                        } else {
                             last_read_error = getLastReadStatusNative();
                             if (last_read_error != 0) {
                                 Log.i(TAG, "headset read error " + last_read_error);
@@ -179,8 +180,6 @@
         mEventThread.start();
     }
 
-
-
     private native String readNative(int timeout_ms);
     private native int getLastReadStatusNative();
 
diff --git a/core/java/android/content/ClippedData.aidl b/core/java/android/content/ClipData.aidl
similarity index 96%
rename from core/java/android/content/ClippedData.aidl
rename to core/java/android/content/ClipData.aidl
index 5246526..5fc12ce 100644
--- a/core/java/android/content/ClippedData.aidl
+++ b/core/java/android/content/ClipData.aidl
@@ -16,4 +16,4 @@
 
 package android.content;
 
-parcelable ClippedData;
+parcelable ClipData;
diff --git a/core/java/android/content/ClippedData.java b/core/java/android/content/ClipData.java
similarity index 71%
rename from core/java/android/content/ClippedData.java
rename to core/java/android/content/ClipData.java
index c3f0237..0c0c0ec 100644
--- a/core/java/android/content/ClippedData.java
+++ b/core/java/android/content/ClipData.java
@@ -37,6 +37,14 @@
  * each of which can hold one or more representations of an item of data.
  * For display to the user, it also has a label and iconic representation.</p>
  *
+ * <p>A ClipData is a sub-class of {@link ClipDescription}, which describes
+ * important meta-data about the clip.  In particular, its {@link #getMimeType(int)}
+ * must return correct MIME type(s) describing the data in the clip.  For help
+ * in correctly constructing a clip with the correct MIME type, use
+ * {@link #newPlainText(CharSequence, Bitmap, CharSequence)},
+ * {@link #newUri(ContentResolver, CharSequence, Bitmap, Uri)}, and
+ * {@link #newIntent(CharSequence, Bitmap, Intent)}.
+ *
  * <p>Each Item instance can be one of three main classes of data: a simple
  * CharSequence of text, a single Intent object, or a Uri.  See {@link Item}
  * for more details.
@@ -53,11 +61,16 @@
  *
  * <p>If all you want is the textual representation of the clipped data, you
  * can use the convenience method {@link Item#coerceToText Item.coerceToText}.
+ * In this case there is generally no need to worry about the MIME types
+ * reported by {@link #getMimeType(int)}, since any clip item an always be
+ * converted to a string.
  *
  * <p>More complicated exchanges will be done through URIs, in particular
  * "content:" URIs.  A content URI allows the recipient of a ClippedData item
  * to interact closely with the ContentProvider holding the data in order to
- * negotiate the transfer of that data.
+ * negotiate the transfer of that data.  The clip must also be filled in with
+ * the available MIME types; {@link #newUri(ContentResolver, CharSequence, Bitmap, Uri)}
+ * will take care of correctly doing this.
  *
  * <p>For example, here is the paste function of a simple NotePad application.
  * When retrieving the data from the clipboard, it can do either two things:
@@ -120,9 +133,12 @@
  * into an editor), then {@link Item#coerceToText(Context)} will ask the content
  * provider for the clip URI as text and successfully paste the entire note.
  */
-public class ClippedData implements Parcelable {
-    CharSequence mLabel;
-    Bitmap mIcon;
+public class ClipData extends ClipDescription {
+    static final String[] MIMETYPES_TEXT_PLAIN = new String[] { MIMETYPE_TEXT_PLAIN };
+    static final String[] MIMETYPES_TEXT_URILIST = new String[] { MIMETYPE_TEXT_URILIST };
+    static final String[] MIMETYPES_TEXT_INTENT = new String[] { MIMETYPE_TEXT_INTENT };
+
+    final Bitmap mIcon;
 
     final ArrayList<Item> mItems = new ArrayList<Item>();
 
@@ -146,28 +162,34 @@
      * </ul>
      */
     public static class Item {
-        CharSequence mText;
-        Intent mIntent;
-        Uri mUri;
+        final CharSequence mText;
+        final Intent mIntent;
+        final Uri mUri;
 
         /**
          * Create an Item consisting of a single block of (possibly styled) text.
          */
         public Item(CharSequence text) {
             mText = text;
+            mIntent = null;
+            mUri = null;
         }
 
         /**
          * Create an Item consisting of an arbitrary Intent.
          */
         public Item(Intent intent) {
+            mText = null;
             mIntent = intent;
+            mUri = null;
         }
 
         /**
          * Create an Item consisting of an arbitrary URI.
          */
         public Item(Uri uri) {
+            mText = null;
+            mIntent = null;
             mUri = uri;
         }
 
@@ -292,19 +314,103 @@
      * Create a new clip.
      *
      * @param label Label to show to the user describing this clip.
+     * @param mimeTypes An array of MIME types this data is available as.
      * @param icon Bitmap providing the user with an iconing representation of
      * the clip.
      * @param item The contents of the first item in the clip.
      */
-    public ClippedData(CharSequence label, Bitmap icon, Item item) {
+    public ClipData(CharSequence label, String[] mimeTypes, Bitmap icon, Item item) {
+        super(label, mimeTypes);
         if (item == null) {
             throw new NullPointerException("item is null");
         }
-        mLabel = label;
         mIcon = icon;
         mItems.add(item);
     }
 
+    /**
+     * Create a new ClipData holding data of the type {@link #MIMETYPE_TEXT_PLAIN}.
+     *
+     * @param label User-visible label for the clip data.
+     * @param icon Iconic representation of the clip data.
+     * @param text The actual text in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newPlainText(CharSequence label, Bitmap icon, CharSequence text) {
+        Item item = new Item(text);
+        return new ClipData(label, MIMETYPES_TEXT_PLAIN, icon, item);
+    }
+
+    /**
+     * Create a new ClipData holding an Intent with MIME type {@link #MIMETYPE_TEXT_INTENT}.
+     *
+     * @param label User-visible label for the clip data.
+     * @param icon Iconic representation of the clip data.
+     * @param intent The actual Intent in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newIntent(CharSequence label, Bitmap icon, Intent intent) {
+        Item item = new Item(intent);
+        return new ClipData(label, MIMETYPES_TEXT_INTENT, icon, item);
+    }
+
+    /**
+     * Create a new ClipData holding a URI.  If the URI is a content: URI,
+     * this will query the content provider for the MIME type of its data and
+     * use that as the MIME type.  Otherwise, it will use the MIME type
+     * {@link #MIMETYPE_TEXT_URILIST}.
+     *
+     * @param resolver ContentResolver used to get information about the URI.
+     * @param label User-visible label for the clip data.
+     * @param icon Iconic representation of the clip data.
+     * @param uri The URI in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newUri(ContentResolver resolver, CharSequence label,
+            Bitmap icon, Uri uri) {
+        Item item = new Item(uri);
+        String[] mimeTypes = null;
+        if ("content".equals(uri.getScheme())) {
+            String realType = resolver.getType(uri);
+            mimeTypes = resolver.getStreamTypes(uri, "*/*");
+            if (mimeTypes == null) {
+                if (realType != null) {
+                    mimeTypes = new String[] { realType, MIMETYPE_TEXT_URILIST };
+                }
+            } else {
+                String[] tmp = new String[mimeTypes.length + (realType != null ? 2 : 1)];
+                int i = 0;
+                if (realType != null) {
+                    tmp[0] = realType;
+                    i++;
+                }
+                System.arraycopy(mimeTypes, 0, tmp, i, mimeTypes.length);
+                tmp[i + mimeTypes.length] = MIMETYPE_TEXT_URILIST;
+                mimeTypes = tmp;
+            }
+        }
+        if (mimeTypes == null) {
+            mimeTypes = MIMETYPES_TEXT_URILIST;
+        }
+        return new ClipData(label, mimeTypes, icon, item);
+    }
+
+    /**
+     * Create a new ClipData holding an URI with MIME type {@link #MIMETYPE_TEXT_URILIST}.
+     * Unlike {@link #newUri(ContentResolver, CharSequence, Bitmap, Uri)}, nothing
+     * is inferred about the URI -- if it is a content: URI holding a bitmap,
+     * the reported type will still be uri-list.  Use this with care!
+     *
+     * @param label User-visible label for the clip data.
+     * @param icon Iconic representation of the clip data.
+     * @param uri The URI in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newRawUri(CharSequence label, Bitmap icon, Uri uri) {
+        Item item = new Item(uri);
+        return new ClipData(label, MIMETYPES_TEXT_URILIST, icon, item);
+    }
+
     public void addItem(Item item) {
         if (item == null) {
             throw new NullPointerException("item is null");
@@ -312,10 +418,6 @@
         mItems.add(item);
     }
 
-    public CharSequence getLabel() {
-        return mLabel;
-    }
-
     public Bitmap getIcon() {
         return mIcon;
     }
@@ -335,7 +437,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        TextUtils.writeToParcel(mLabel, dest, flags);
+        super.writeToParcel(dest, flags);
         if (mIcon != null) {
             dest.writeInt(1);
             mIcon.writeToParcel(dest, flags);
@@ -362,10 +464,12 @@
         }
     }
 
-    ClippedData(Parcel in) {
-        mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+    ClipData(Parcel in) {
+        super(in);
         if (in.readInt() != 0) {
             mIcon = Bitmap.CREATOR.createFromParcel(in);
+        } else {
+            mIcon = null;
         }
         final int N = in.readInt();
         for (int i=0; i<N; i++) {
@@ -376,15 +480,15 @@
         }
     }
 
-    public static final Parcelable.Creator<ClippedData> CREATOR =
-        new Parcelable.Creator<ClippedData>() {
+    public static final Parcelable.Creator<ClipData> CREATOR =
+        new Parcelable.Creator<ClipData>() {
 
-            public ClippedData createFromParcel(Parcel source) {
-                return new ClippedData(source);
+            public ClipData createFromParcel(Parcel source) {
+                return new ClipData(source);
             }
 
-            public ClippedData[] newArray(int size) {
-                return new ClippedData[size];
+            public ClipData[] newArray(int size) {
+                return new ClipData[size];
             }
         };
 }
diff --git a/core/java/android/content/ClippedData.aidl b/core/java/android/content/ClipDescription.aidl
similarity index 95%
copy from core/java/android/content/ClippedData.aidl
copy to core/java/android/content/ClipDescription.aidl
index 5246526..391fd5a 100644
--- a/core/java/android/content/ClippedData.aidl
+++ b/core/java/android/content/ClipDescription.aidl
@@ -16,4 +16,4 @@
 
 package android.content;
 
-parcelable ClippedData;
+parcelable ClipDescription;
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
new file mode 100644
index 0000000..3229f0f
--- /dev/null
+++ b/core/java/android/content/ClipDescription.java
@@ -0,0 +1,206 @@
+/**
+ * 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.content;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Meta-data describing the contents of a {@link #ClipData}.  Provides enough
+ * information to know if you can handle the ClipData, but not the data
+ * itself.
+ */
+public class ClipDescription implements Parcelable {
+    /**
+     * The MIME type for a clip holding plain text.
+     */
+    public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
+
+    /**
+     * The MIME type for a clip holding one or more URIs.  This should be
+     * used for URIs that are meaningful to a user (such as an http: URI).
+     * It should <em>not</em> be used for a content: URI that references some
+     * other piece of data; in that case the MIME type should be the type
+     * of the referenced data.
+     */
+    public static final String MIMETYPE_TEXT_URILIST = "text/uri-list";
+
+    /**
+     * The MIME type for a clip holding an Intent.
+     */
+    public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
+
+    final CharSequence mLabel;
+    final String[] mMimeTypes;
+
+    /**
+     * Create a new clip.
+     *
+     * @param label Label to show to the user describing this clip.
+     * @param mimeTypes An array of MIME types this data is available as.
+     */
+    public ClipDescription(CharSequence label, String[] mimeTypes) {
+        if (mimeTypes == null) {
+            throw new NullPointerException("mimeTypes is null");
+        }
+        mLabel = label;
+        mMimeTypes = mimeTypes;
+    }
+
+    /**
+     * Create a copy of a ClipDescription.
+     */
+    public ClipDescription(ClipDescription o) {
+        mLabel = o.mLabel;
+        mMimeTypes = o.mMimeTypes;
+    }
+
+    /**
+     * Helper to compare two MIME types, where one may be a pattern.
+     * @param concreteType A fully-specified MIME type.
+     * @param desiredType A desired MIME type that may be a pattern such as *\/*.
+     * @return Returns true if the two MIME types match.
+     */
+    public static boolean compareMimeTypes(String concreteType, String desiredType) {
+        final int typeLength = desiredType.length();
+        if (typeLength == 3 && desiredType.equals("*/*")) {
+            return true;
+        }
+
+        final int slashpos = desiredType.indexOf('/');
+        if (slashpos > 0) {
+            if (typeLength == slashpos+2 && desiredType.charAt(slashpos+1) == '*') {
+                if (desiredType.regionMatches(0, concreteType, 0, slashpos+1)) {
+                    return true;
+                }
+            } else if (desiredType.equals(concreteType)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Return the label for this clip.
+     */
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Check whether the clip description contains the given MIME type.
+     *
+     * @param mimeType The desired MIME type.  May be a pattern.
+     * @return Returns true if one of the MIME types in the clip description
+     * matches the desired MIME type, else false.
+     */
+    public boolean hasMimeType(String mimeType) {
+        for (int i=0; i<mMimeTypes.length; i++) {
+            if (compareMimeTypes(mMimeTypes[i], mimeType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Filter the clip description MIME types by the given MIME type.  Returns
+     * all MIME types in the clip that match the given MIME type.
+     *
+     * @param mimeType The desired MIME type.  May be a pattern.
+     * @return Returns an array of all matching MIME types.  If there are no
+     * matching MIME types, null is returned.
+     */
+    public String[] filterMimeTypes(String mimeType) {
+        ArrayList<String> array = null;
+        for (int i=0; i<mMimeTypes.length; i++) {
+            if (compareMimeTypes(mMimeTypes[i], mimeType)) {
+                if (array == null) {
+                    array = new ArrayList<String>();
+                }
+                array.add(mMimeTypes[i]);
+            }
+        }
+        if (array == null) {
+            return null;
+        }
+        String[] rawArray = new String[array.size()];
+        array.toArray(rawArray);
+        return rawArray;
+    }
+
+    /**
+     * Return the number of MIME types the clip is available in.
+     */
+    public int getMimeTypeCount() {
+        return mMimeTypes.length;
+    }
+
+    /**
+     * Return one of the possible clip MIME types.
+     */
+    public String getMimeType(int index) {
+        return mMimeTypes[index];
+    }
+
+    /** @hide */
+    public void validate() {
+        if (mMimeTypes == null) {
+            throw new NullPointerException("null mime types");
+        }
+        if (mMimeTypes.length <= 0) {
+            throw new IllegalArgumentException("must have at least 1 mime type");
+        }
+        for (int i=0; i<mMimeTypes.length; i++) {
+            if (mMimeTypes[i] == null) {
+                throw new NullPointerException("mime type at " + i + " is null");
+            }
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        TextUtils.writeToParcel(mLabel, dest, flags);
+        dest.writeStringArray(mMimeTypes);
+    }
+
+    ClipDescription(Parcel in) {
+        mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mMimeTypes = in.createStringArray();
+    }
+
+    public static final Parcelable.Creator<ClipDescription> CREATOR =
+        new Parcelable.Creator<ClipDescription>() {
+
+            public ClipDescription createFromParcel(Parcel source) {
+                return new ClipDescription(source);
+            }
+
+            public ClipDescription[] newArray(int size) {
+                return new ClipDescription[size];
+            }
+        };
+}
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index d685cf3..0ea0648 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -37,7 +37,7 @@
  * <p>
  * The ClipboardManager API itself is very simple: it consists of methods
  * to atomically get and set the current primary clipboard data.  That data
- * is expressed as a {@link ClippedData} object, which defines the protocol
+ * is expressed as a {@link ClipData} object, which defines the protocol
  * for data exchange between applications.
  *
  * @see android.content.Context#getSystemService
@@ -96,7 +96,7 @@
      *
      * @param clip The clipped data item to set.
      */
-    public void setPrimaryClip(ClippedData clip) {
+    public void setPrimaryClip(ClipData clip) {
         try {
             getService().setPrimaryClip(clip);
         } catch (RemoteException e) {
@@ -106,7 +106,7 @@
     /**
      * Returns the current primary clip on the clipboard.
      */
-    public ClippedData getPrimaryClip() {
+    public ClipData getPrimaryClip() {
         try {
             return getService().getPrimaryClip();
         } catch (RemoteException e) {
@@ -115,6 +115,18 @@
     }
 
     /**
+     * Returns a description of the current primary clip on the clipboard
+     * but not a copy of its data.
+     */
+    public ClipDescription getPrimaryClipDescription() {
+        try {
+            return getService().getPrimaryClipDescription();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Returns true if there is currently a primary clip on the clipboard.
      */
     public boolean hasPrimaryClip() {
@@ -156,7 +168,7 @@
      * the primary clip and tries to coerce it to a string.
      */
     public CharSequence getText() {
-        ClippedData clip = getPrimaryClip();
+        ClipData clip = getPrimaryClip();
         if (clip != null && clip.getItemCount() > 0) {
             return clip.getItem(0).coerceToText(mContext);
         }
@@ -164,12 +176,12 @@
     }
 
     /**
-     * @deprecated Use {@link #setPrimaryClip(ClippedData)} instead.  This
+     * @deprecated Use {@link #setPrimaryClip(ClipData)} instead.  This
      * creates a ClippedItem holding the given text and sets it as the
      * primary clip.  It has no label or icon.
      */
     public void setText(CharSequence text) {
-        setPrimaryClip(new ClippedData(null, null, new ClippedData.Item(text)));
+        setPrimaryClip(ClipData.newPlainText(null, null, text));
     }
 
     /**
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 22bce05..d3c1b4e 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -91,6 +91,7 @@
     private String mReadPermission;
     private String mWritePermission;
     private PathPermission[] mPathPermissions;
+    private boolean mExported;
 
     private Transport mTransport = new Transport();
 
@@ -274,9 +275,9 @@
             final Context context = getContext();
             final String rperm = getReadPermission();
             final int pid = Binder.getCallingPid();
-            if (rperm == null
+            if (mExported && (rperm == null
                     || context.checkPermission(rperm, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED) {
+                    == PackageManager.PERMISSION_GRANTED)) {
                 return;
             }
             
@@ -320,9 +321,9 @@
             final Context context = getContext();
             final String wperm = getWritePermission();
             final int pid = Binder.getCallingPid();
-            if (wperm == null
+            if (mExported && (wperm == null
                     || context.checkPermission(wperm, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED) {
+                    == PackageManager.PERMISSION_GRANTED)) {
                 return true;
             }
             
@@ -766,32 +767,6 @@
     }
 
     /**
-     * Helper to compare two MIME types, where one may be a pattern.
-     * @param concreteType A fully-specified MIME type.
-     * @param desiredType A desired MIME type that may be a pattern such as *\/*.
-     * @return Returns true if the two MIME types match.
-     */
-    public static boolean compareMimeTypes(String concreteType, String desiredType) {
-        final int typeLength = desiredType.length();
-        if (typeLength == 3 && desiredType.equals("*/*")) {
-            return true;
-        }
-
-        final int slashpos = desiredType.indexOf('/');
-        if (slashpos > 0) {
-            if (typeLength == slashpos+2 && desiredType.charAt(slashpos+1) == '*') {
-                if (desiredType.regionMatches(0, concreteType, 0, slashpos+1)) {
-                    return true;
-                }
-            } else if (desiredType.equals(concreteType)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
      * Called by a client to determine the types of data streams that this
      * content provider supports for the given URI.  The default implementation
      * returns null, meaning no types.  If your content provider stores data
@@ -808,7 +783,7 @@
      *
      * @see #getType(Uri)
      * @see #openTypedAssetFile(Uri, String, Bundle)
-     * @see #compareMimeTypes(String, String)
+     * @see ClipDescription#compareMimeTypes(String, String)
      */
     public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
         return null;
@@ -824,7 +799,7 @@
      * result of {@link #getType(Uri)} and, if the match, simple calls
      * {@link #openAssetFile(Uri, String)}.
      *
-     * <p>See {@link ClippedData} for examples of the use and implementation
+     * <p>See {@link ClipData} for examples of the use and implementation
      * of this method.
      *
      * @param uri The data in the content provider being queried.
@@ -847,7 +822,7 @@
      *
      * @see #getStreamTypes(Uri, String)
      * @see #openAssetFile(Uri, String)
-     * @see #compareMimeTypes(String, String)
+     * @see ClipDescription#compareMimeTypes(String, String)
      */
     public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
             throws FileNotFoundException {
@@ -856,7 +831,7 @@
             return openAssetFile(uri, "r");
         }
         String baseType = getType(uri);
-        if (baseType != null && compareMimeTypes(baseType, mimeTypeFilter)) {
+        if (baseType != null && ClipDescription.compareMimeTypes(baseType, mimeTypeFilter)) {
             // Use old untyped open call if this provider has a type for this
             // URI and it matches the request.
             return openAssetFile(uri, "r");
@@ -972,6 +947,7 @@
                 setReadPermission(info.readPermission);
                 setWritePermission(info.writePermission);
                 setPathPermissions(info.pathPermissions);
+                mExported = info.exported;
             }
             ContentProvider.this.onCreate();
         }
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index e6dedc13..ce047f6 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -497,6 +497,10 @@
         return (ArrayList<String>) mValues.get(key);
     }
 
+    /**
+     * Returns a string containing a concise, human-readable description of this object.
+     * @return a printable representation of this object.
+     */
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl
index b4534a9..3e1fe55 100644
--- a/core/java/android/content/IClipboard.aidl
+++ b/core/java/android/content/IClipboard.aidl
@@ -16,7 +16,8 @@
 
 package android.content;
 
-import android.content.ClippedData;
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.IOnPrimaryClipChangedListener;
 
 /**
@@ -25,8 +26,9 @@
  * {@hide}
  */
 interface IClipboard {
-    void setPrimaryClip(in ClippedData clip);
-    ClippedData getPrimaryClip();
+    void setPrimaryClip(in ClipData clip);
+    ClipData getPrimaryClip();
+    ClipDescription getPrimaryClipDescription();
     boolean hasPrimaryClip();
     void addPrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener);
     void removePrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener);
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 5847216..6ee716a 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -163,14 +163,47 @@
          * {@link SharedPreferences} object it is editing.  This atomically
          * performs the requested modifications, replacing whatever is currently
          * in the SharedPreferences.
-         * 
+         *
          * <p>Note that when two editors are modifying preferences at the same
          * time, the last one to call commit wins.
-         * 
+         *
+         * <p>If you don't care about the return value and you're
+         * using this from your application's main thread, consider
+         * using {@link #startCommit} instead.
+         *
          * @return Returns true if the new values were successfully written
          * to persistent storage.
          */
         boolean commit();
+
+        /**
+         * Commit your preferences changes back from this Editor to the
+         * {@link SharedPreferences} object it is editing.  This atomically
+         * performs the requested modifications, replacing whatever is currently
+         * in the SharedPreferences.
+         *
+         * <p>Note that when two editors are modifying preferences at the same
+         * time, the last one to call commit wins.
+         *
+         * <p>Unlike {@link #commit}, which writes its preferences out
+         * to persistent storage synchronously, {@link #startCommit}
+         * commits its changes to the in-memory
+         * {@link SharedPreferences} immediately but starts an
+         * asynchronous commit to disk and you won't be notified of
+         * any failures.  If another editor on this
+         * {@link SharedPreferences} does a regular {@link #commit}
+         * while a {@link #startCommit} is still outstanding, the
+         * {@link #commit} will block until all async commits are
+         * completed as well as the commit itself.
+         *
+         * <p>If you call this from an {@link android.app.Activity},
+         * the base class will wait for any async commits to finish in
+         * its {@link android.app.Activity#onPause}.</p>
+         *
+         * @return Returns true if the new values were successfully written
+         * to persistent storage.
+         */
+        void startCommit();
     }
 
     /**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 7901b155..c812f36 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -273,17 +273,6 @@
     public static final int FLAG_SUPPORTS_XLARGE_SCREENS = 1<<19;
     
     /**
-     * Value for {@link #flags}: set to <code>true</code> if the application
-     * has reported that it is heavy-weight, and thus can not participate in
-     * the normal application lifecycle.
-     * 
-     * <p>Comes from the
-     * {@link android.R.styleable#AndroidManifestApplication_heavyWeight android:heavyWeight}
-     * attribute of the &lt;application&gt; tag.
-     */
-    public static final int FLAG_HEAVY_WEIGHT = 1<<20;
-    
-    /**
      * Value for {@link #flags}: this is true if the application has set
      * its android:neverEncrypt to true, false otherwise. It is used to specify
      * that this package specifically "opts-out" of a secured file system solution,
@@ -310,6 +299,19 @@
     public static final int FLAG_NATIVE_DEBUGGABLE = 1<<28;
 
     /**
+     * Value for {@link #flags}: set to <code>true</code> if the application
+     * has reported that it is heavy-weight, and thus can not participate in
+     * the normal application lifecycle.
+     *
+     * <p>Comes from the
+     * {@link android.R.styleable#AndroidManifestApplication_heavyWeight android:heavyWeight}
+     * attribute of the &lt;application&gt; tag.
+     *
+     * {@hide}
+     */
+    public static final int CANT_SAVE_STATE = 1<<27;
+
+    /**
      * Flags associated with the application.  Any combination of
      * {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
      * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
@@ -389,15 +391,17 @@
         if (permission != null) {
             pw.println(prefix + "permission=" + permission);
         }
-        pw.println(prefix + "uid=" + uid + " taskAffinity=" + taskAffinity);
-        if (theme != 0) {
-            pw.println(prefix + "theme=0x" + Integer.toHexString(theme));
-        }
-        pw.println(prefix + "flags=0x" + Integer.toHexString(flags)
-                + " processName=" + processName);
+        pw.println(prefix + "processName=" + processName);
+        pw.println(prefix + "taskAffinity=" + taskAffinity);
+        pw.println(prefix + "uid=" + uid + " flags=0x" + Integer.toHexString(flags)
+                + " theme=0x" + Integer.toHexString(theme));
         pw.println(prefix + "sourceDir=" + sourceDir);
-        pw.println(prefix + "publicSourceDir=" + publicSourceDir);
-        pw.println(prefix + "resourceDirs=" + resourceDirs);
+        if (!sourceDir.equals(publicSourceDir)) {
+            pw.println(prefix + "publicSourceDir=" + publicSourceDir);
+        }
+        if (resourceDirs != null) {
+            pw.println(prefix + "resourceDirs=" + resourceDirs);
+        }
         pw.println(prefix + "dataDir=" + dataDir);
         if (sharedLibraryFiles != null) {
             pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 155c86c..92a8a99 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1494,12 +1494,18 @@
             ai.nonLocalizedLabel = v.coerceToString();
         }
 
+        int defaultTheme = 0;
+        if (owner.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+            // As of honeycomb, the default application theme is holographic.
+            defaultTheme = android.R.style.Theme_Holo;
+        }
+
         ai.icon = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
         ai.logo = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestApplication_logo, 0);
         ai.theme = sa.getResourceId(
-                com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
+                com.android.internal.R.styleable.AndroidManifestApplication_theme, defaultTheme);
         ai.descriptionRes = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
 
@@ -1601,15 +1607,17 @@
             ai.enabled = sa.getBoolean(
                     com.android.internal.R.styleable.AndroidManifestApplication_enabled, true);
             
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestApplication_heavyWeight,
-                    false)) {
-                ai.flags |= ApplicationInfo.FLAG_HEAVY_WEIGHT;
-                
-                // A heavy-weight application can not be in a custom process.
-                // We can do direct compare because we intern all strings.
-                if (ai.processName != null && ai.processName != ai.packageName) {
-                    outError[0] = "Heavy-weight applications can not use custom processes";
+            if (false) {
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
+                        false)) {
+                    ai.flags |= ApplicationInfo.CANT_SAVE_STATE;
+
+                    // A heavy-weight application can not be in a custom process.
+                    // We can do direct compare because we intern all strings.
+                    if (ai.processName != null && ai.processName != ai.packageName) {
+                        outError[0] = "cantSaveState applications can not use custom processes";
+                    }
                 }
             }
         }
@@ -1915,7 +1923,7 @@
 
         sa.recycle();
 
-        if (receiver && (owner.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+        if (receiver && (owner.applicationInfo.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
             // A heavy-weight application can not have receives in its main process
             // We can do direct compare because we intern all strings.
             if (a.info.processName == owner.packageName) {
@@ -2205,7 +2213,7 @@
 
         sa.recycle();
 
-        if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+        if ((owner.applicationInfo.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
             // A heavy-weight application can not have providers in its main process
             // We can do direct compare because we intern all strings.
             if (p.info.processName == owner.packageName) {
@@ -2446,7 +2454,7 @@
 
         sa.recycle();
 
-        if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+        if ((owner.applicationInfo.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
             // A heavy-weight application can not have services in its main process
             // We can do direct compare because we intern all strings.
             if (s.info.processName == owner.packageName) {
diff --git a/core/java/android/content/res/ObbInfo.java b/core/java/android/content/res/ObbInfo.java
index b18d784..838c5ff 100644
--- a/core/java/android/content/res/ObbInfo.java
+++ b/core/java/android/content/res/ObbInfo.java
@@ -25,6 +25,9 @@
  * @hide
  */
 public class ObbInfo implements Parcelable {
+    /** Flag noting that this OBB is an overlay patch for a base OBB. */
+    public static final int OBB_OVERLAY = 1 << 0;
+
     /**
      * The name of the package to which the OBB file belongs.
      */
@@ -35,13 +38,26 @@
      */
     public int version;
 
+    /**
+     * The flags relating to the OBB.
+     */
+    public int flags;
+
     public ObbInfo() {
     }
 
     public String toString() {
-        return "ObbInfo{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " packageName=" + packageName + ",version=" + version + "}";
+        StringBuilder sb = new StringBuilder();
+        sb.append("ObbInfo{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" packageName=");
+        sb.append(packageName);
+        sb.append(",version=");
+        sb.append(version);
+        sb.append(",flags=");
+        sb.append(flags);
+        sb.append('}');
+        return sb.toString();
     }
 
     public int describeContents() {
@@ -51,6 +67,7 @@
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeString(packageName);
         dest.writeInt(version);
+        dest.writeInt(flags);
     }
 
     public static final Parcelable.Creator<ObbInfo> CREATOR
@@ -67,5 +84,6 @@
     private ObbInfo(Parcel source) {
         packageName = source.readString();
         version = source.readInt();
+        flags = source.readInt();
     }
 }
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index b0c149d..37fdeb6 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -708,9 +708,7 @@
         outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
         outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
         outValue.density = data[index+AssetManager.STYLE_DENSITY];
-        if (type == TypedValue.TYPE_STRING) {
-            outValue.string = loadStringValueAt(index);
-        }
+        outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
         return true;
     }
 
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 03aa968..38d6526 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -651,14 +651,40 @@
      * @return the number of rows in the table
      */
     public static long queryNumEntries(SQLiteDatabase db, String table) {
-        Cursor cursor = db.query(table, countProjection,
-                null, null, null, null, null);
-        try {
-            cursor.moveToFirst();
-            return cursor.getLong(0);
-        } finally {
-            cursor.close();
-        }
+        return queryNumEntries(db, table, null, null);
+    }
+
+    /**
+     * Query the table for the number of rows in the table.
+     * @param db the database the table is in
+     * @param table the name of the table to query
+     * @param selection A filter declaring which rows to return,
+     *              formatted as an SQL WHERE clause (excluding the WHERE itself).
+     *              Passing null will count all rows for the given table
+     * @return the number of rows in the table filtered by the selection
+     */
+    public static long queryNumEntries(SQLiteDatabase db, String table, String selection) {
+        return queryNumEntries(db, table, selection, null);
+    }
+
+    /**
+     * Query the table for the number of rows in the table.
+     * @param db the database the table is in
+     * @param table the name of the table to query
+     * @param selection A filter declaring which rows to return,
+     *              formatted as an SQL WHERE clause (excluding the WHERE itself).
+     *              Passing null will count all rows for the given table
+     * @param selectionArgs You may include ?s in selection,
+     *              which will be replaced by the values from selectionArgs,
+     *              in order that they appear in the selection.
+     *              The values will be bound as Strings.
+     * @return the number of rows in the table filtered by the selection
+     */
+    public static long queryNumEntries(SQLiteDatabase db, String table, String selection,
+            String[] selectionArgs) {
+        String s = (!TextUtils.isEmpty(selection)) ? " where " + selection : "";
+        return longForQuery(db, "select count(*) from " + table + s,
+                    selectionArgs);
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index dc3b469..b83d5ad 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2066,11 +2066,9 @@
      * mapping is NOT replaced with the new mapping).
      */
     /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
-        SQLiteCompiledSql compiledSql = null;
         synchronized(mCompiledQueries) {
             // don't insert the new mapping if a mapping already exists
-            compiledSql = mCompiledQueries.get(sql);
-            if (compiledSql != null) {
+            if (mCompiledQueries.containsKey(sql)) {
                 return;
             }
 
@@ -2392,10 +2390,9 @@
         ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
         // make a local copy of mActiveDatabases - so that this method is not competing
         // for synchronization lock on mActiveDatabases
-        ArrayList<WeakReference<SQLiteDatabase>> tempList =
-                new ArrayList<WeakReference<SQLiteDatabase>>();
+        ArrayList<WeakReference<SQLiteDatabase>> tempList;
         synchronized(mActiveDatabases) {
-            Collections.copy(tempList, mActiveDatabases);
+            tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone();
         }
         for (WeakReference<SQLiteDatabase> w : tempList) {
             SQLiteDatabase db = w.get();
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index d8cce21..ccf8d68 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -121,8 +121,13 @@
      * @return a read/write database object valid until {@link #close} is called
      */
     public synchronized SQLiteDatabase getWritableDatabase() {
-        if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
-            return mDatabase;  // The database is already open for business
+        if (mDatabase != null) {
+            if (!mDatabase.isOpen()) {
+                // darn! the user closed the database by calling mDatabase.close()
+                mDatabase = null;
+            } else if (!mDatabase.isReadOnly()) {
+                return mDatabase;  // The database is already open for business
+            }
         }
 
         if (mIsInitializing) {
@@ -207,8 +212,13 @@
      *     or {@link #close} is called.
      */
     public synchronized SQLiteDatabase getReadableDatabase() {
-        if (mDatabase != null && mDatabase.isOpen()) {
-            return mDatabase;  // The database is already open for business
+        if (mDatabase != null) {
+            if (!mDatabase.isOpen()) {
+                // darn! the user closed the database by calling mDatabase.close()
+                mDatabase = null;
+            } else {
+                return mDatabase;  // The database is already open for business
+            }
         }
 
         if (mIsInitializing) {
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 231d8e6..9b7d823 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -158,18 +158,18 @@
 
     @Override
     protected void onAllReferencesReleased() {
-        releaseCompiledSqlIfNotInCache();
+        release();
         mDatabase.removeSQLiteClosable(this);
         mDatabase.releaseReference();
     }
 
     @Override
     protected void onAllReferencesReleasedFromContainer() {
-        releaseCompiledSqlIfNotInCache();
+        release();
         mDatabase.releaseReference();
     }
 
-    /* package */ synchronized void releaseCompiledSqlIfNotInCache() {
+    /* package */ synchronized void release() {
         if (mCompiledSql == null) {
             return;
         }
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 14de60f..dc07db0 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -265,7 +265,7 @@
         clearBindings();
         // release the compiled sql statement so that the caller's SQLiteStatement no longer
         // has a hard reference to a database object that may get deallocated at any point.
-        releaseCompiledSqlIfNotInCache();
+        release();
         // restore the database connection handle to the original value
         mDatabase = mOrigDb;
         nHandle = mDatabase.mNativeHandle;
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index a0abc6c..e432a47 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -960,6 +960,7 @@
         private static final String KEY_PREVIEW_SIZE = "preview-size";
         private static final String KEY_PREVIEW_FORMAT = "preview-format";
         private static final String KEY_PREVIEW_FRAME_RATE = "preview-frame-rate";
+        private static final String KEY_PREVIEW_FPS_RANGE = "preview-fps-range";
         private static final String KEY_PICTURE_SIZE = "picture-size";
         private static final String KEY_PICTURE_FORMAT = "picture-format";
         private static final String KEY_JPEG_THUMBNAIL_SIZE = "jpeg-thumbnail-size";
@@ -1219,6 +1220,22 @@
          */
         public static final String METERING_MODE_SPOT = "spot";
 
+        /**
+         * The array index of minimum preview fps for use with {@link
+         * #getPreviewFpsRange(int[])} or {@link
+         * #getSupportedPreviewFpsRange()}.
+         * @hide
+         */
+        public static final int PREVIEW_FPS_MIN_INDEX = 0;
+
+        /**
+         * The array index of maximum preview fps for use with {@link
+         * #getPreviewFpsRange(int[])} or {@link
+         * #getSupportedPreviewFpsRange()}.
+         * @hide
+         */
+        public static final int PREVIEW_FPS_MAX_INDEX = 1;
+
         // Formats for setPreviewFormat and setPictureFormat.
         private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp";
         private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp";
@@ -1482,6 +1499,64 @@
         }
 
         /**
+         * Sets the maximum and maximum preview fps. This controls the rate of
+         * preview frames received in {@link #PreviewCallback}. The minimum and
+         * maximum preview fps must be one of the elements from {@link
+         * #getSupportedPreviewFpsRange}.
+         *
+         * @param min the minimum preview fps (scaled by 1000).
+         * @param max the maximum preview fps (scaled by 1000).
+         * @throws RuntimeException if fps range is invalid.
+         * @see #setPreviewCallbackWithBuffer(Camera.PreviewCallback)
+         * @see #getSupportedPreviewFpsRange()
+         * @hide
+         */
+        public void setPreviewFpsRange(int min, int max) {
+            set(KEY_PREVIEW_FPS_RANGE, "" + min + "," + max);
+        }
+
+        /**
+         * Returns the current minimum and maximum preview fps. The values are
+         * one of the elements returned by {@link #getSupportedPreviewFpsRange}.
+         *
+         * @return range the minimum and maximum preview fps (scaled by 1000).
+         * @see #PREVIEW_FPS_MIN_INDEX
+         * @see #PREVIEW_FPS_MAX_INDEX
+         * @see #getSupportedPreviewFpsRange()
+         * @hide
+         */
+        public void getPreviewFpsRange(int[] range) {
+            if (range == null || range.length != 2) {
+                throw new IllegalArgumentException(
+                        "range must be an float array with two elements.");
+            }
+            splitInt(get(KEY_PREVIEW_FPS_RANGE), range);
+        }
+
+        /**
+         * Gets the supported preview fps (frame-per-second) ranges. Each range
+         * contains a minimum fps and maximum fps. If minimum fps equals to
+         * maximum fps, the camera outputs frames in fixed frame rate. If not,
+         * the camera outputs frames in auto frame rate. The actual frame rate
+         * fluctuates between the minimum and the maximum. The values are
+         * multiplied by 1000 and represented in integers. For example, if frame
+         * rate is 26.623 frames per second, the value is 26623.
+         *
+         * @return a list of supported preview fps ranges. This method returns a
+         *         list with at least one element. Every element is an int array
+         *         of two values - minimum fps and maximum fps. The list is
+         *         sorted from small to large (first by maximum fps and then
+         *         minimum fps).
+         * @see #PREVIEW_FPS_MIN_INDEX
+         * @see #PREVIEW_FPS_MAX_INDEX
+         * @hide
+         */
+        public List<int[]> getSupportedPreviewFpsRange() {
+            String str = get(KEY_PREVIEW_FPS_RANGE + SUPPORTED_VALUES_SUFFIX);
+            return splitRange(str);
+        }
+
+        /**
          * Sets the image format for preview pictures.
          * <p>If this is never called, the default format will be
          * {@link android.graphics.ImageFormat#NV21}, which
@@ -2184,10 +2259,7 @@
                 throw new IllegalArgumentException(
                         "output must be an float array with three elements.");
             }
-            List<Float> distances = splitFloat(get(KEY_FOCUS_DISTANCES));
-            output[0] = distances.get(0);
-            output[1] = distances.get(1);
-            output[2] = distances.get(2);
+            splitFloat(get(KEY_FOCUS_DISTANCES), output);
         }
 
         /**
@@ -2255,19 +2327,27 @@
             return substrings;
         }
 
-        // Splits a comma delimited string to an ArrayList of Float.
-        // Return null if the passing string is null or the size is 0.
-        private ArrayList<Float> splitFloat(String str) {
-            if (str == null) return null;
+        private void splitInt(String str, int[] output) {
+            if (str == null) return;
 
             StringTokenizer tokenizer = new StringTokenizer(str, ",");
-            ArrayList<Float> substrings = new ArrayList<Float>();
+            int index = 0;
             while (tokenizer.hasMoreElements()) {
                 String token = tokenizer.nextToken();
-                substrings.add(Float.parseFloat(token));
+                output[index++] = Integer.parseInt(token);
             }
-            if (substrings.size() == 0) return null;
-            return substrings;
+        }
+
+        // Splits a comma delimited string to an ArrayList of Float.
+        private void splitFloat(String str, float[] output) {
+            if (str == null) return;
+
+            StringTokenizer tokenizer = new StringTokenizer(str, ",");
+            int index = 0;
+            while (tokenizer.hasMoreElements()) {
+                String token = tokenizer.nextToken();
+                output[index++] = Float.parseFloat(token);
+            }
         }
 
         // Returns the value of a float parameter.
@@ -2318,5 +2398,30 @@
             Log.e(TAG, "Invalid size parameter string=" + str);
             return null;
         }
+
+        // Splits a comma delimited string to an ArrayList of int array.
+        // Example string: "(10000,26623),(10000,30000)". Return null if the
+        // passing string is null or the size is 0.
+        private ArrayList<int[]> splitRange(String str) {
+            if (str == null || str.charAt(0) != '('
+                    || str.charAt(str.length() - 1) != ')') {
+                Log.e(TAG, "Invalid range list string=" + str);
+                return null;
+            }
+
+            ArrayList<int[]> rangeList = new ArrayList<int[]>();
+            int endIndex, fromIndex = 1;
+            do {
+                int[] range = new int[2];
+                endIndex = str.indexOf("),(", fromIndex);
+                if (endIndex == -1) endIndex = str.length() - 1;
+                splitInt(str.substring(fromIndex, endIndex), range);
+                rangeList.add(range);
+                fromIndex = endIndex + 3;
+            } while (endIndex != str.length() - 1);
+
+            if (rangeList.size() == 0) return null;
+            return rangeList;
+        }
     };
 }
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index b49a409..f2b907b 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -77,6 +77,27 @@
     public static final int TYPE_PROXIMITY = 8;
 
     /**
+     * A constant describing a gravity sensor type.
+     * See {@link android.hardware.SensorEvent SensorEvent}
+     * for more details.
+     */
+    public static final int TYPE_GRAVITY = 9;
+
+    /**
+     * A constant describing a linear acceleration sensor type.
+     * See {@link android.hardware.SensorEvent SensorEvent}
+     * for more details.
+     */
+    public static final int TYPE_LINEAR_ACCELERATION = 10;
+
+    /**
+     * A constant describing a rotation vector sensor type.
+     * See {@link android.hardware.SensorEvent SensorEvent}
+     * for more details.
+     */
+    public static final int TYPE_ROTATION_VECTOR = 11;
+
+    /** 
      * A constant describing all sensor types.
      */
     public static final int TYPE_ALL = -1;
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index aaf3898..2c5c909 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -133,6 +133,16 @@
      * All values are in micro-Tesla (uT) and measure the ambient magnetic field
      * in the X, Y and Z axis.
      * 
+     * <h4>{@link android.hardware.Sensor#TYPE_GYROSCOPE Sensor.TYPE_GYROSCOPE}:</h4>
+     *  All values are in radians/second and measure the rate of rotation
+     *  around the X, Y and Z axis. The coordinate system is the same as is
+     *  used for the acceleration sensor.  Rotation is positive in the counter-clockwise
+     *  direction.  That is, an observer looking from some positive location on the x, y.
+     *  or z axis at a device positioned on the origin would report positive rotation
+     *  if the device appeared to be rotating counter clockwise.  Note that this is the
+     *  standard mathematical definition of positive rotation and does not agree with the
+     *  definition of roll given earlier.
+     *
      * <h4>{@link android.hardware.Sensor#TYPE_LIGHT Sensor.TYPE_LIGHT}:</h4>
      * 
      * <ul>
@@ -155,6 +165,27 @@
      * the <i>far</i> state and a lesser value in the <i>near</i> state.
      * </p>
      * 
+     *  <h4>{@link android.hardware.Sensor#TYPE_GRAVITY Sensor.TYPE_GRAVITY}:</h4>
+     *  A three dimensional vector indicating the direction and magnitude of gravity.  Units
+     *  are m/s^2.  The coordinate system is the same as is used by the acceleration sensor.
+     *
+     *  <h4>{@link android.hardware.Sensor#TYPE_LINEAR_ACCELERATION Sensor.TYPE_LINEAR_ACCELERATION}:</h4>
+     *  A three dimensional vector indicating acceleration along each device axis, not including
+     *  gravity.  All values have units of m/s^2.  The coordinate system is the same as is used by the
+     * acceleration sensor.
+     *
+     *  <h4>{@link android.hardware.Sensor#TYPE_ROTATION_VECTOR Sensor.TYPE_ROTATION_VECTOR}:</h4>
+     *  The rotation vector represents the orientation of the device as a combination of an angle
+     *  and an axis, in which the device has rotated through an angle theta around an axis
+     *  <x, y, z>. The three elements of the rotation vector are
+     *  <x*sin(theta/2), y*sin(theta/2), z*sin(theta/2)>, such that the magnitude of the rotation
+     *  vector is equal to sin(theta/2), and the direction of the rotation vector is equal to the
+     *  direction of the axis of rotation. The three elements of the rotation vector are equal to
+     *  the last three components of a unit quaternion
+     *  <cos(theta/2), x*sin(theta/2), y*sin(theta/2), z*sin(theta/2)>.  Elements of the rotation
+     *  vector are unitless.  The x,y, and z axis are defined in the same way as the acceleration
+     *  sensor.
+     *
      * <h4>{@link android.hardware.Sensor#TYPE_ORIENTATION
      * Sensor.TYPE_ORIENTATION}:</h4> All values are angles in degrees.
      * 
@@ -201,6 +232,7 @@
      * @see SensorEvent
      * @see GeomagneticField
      */
+
     public final float[] values;
 
     /**
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index d2c3eaa..0068724 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -34,11 +34,19 @@
 import java.util.List;
 
 /**
+ * <p>
  * SensorManager lets you access the device's {@link android.hardware.Sensor
  * sensors}. Get an instance of this class by calling
  * {@link android.content.Context#getSystemService(java.lang.String)
  * Context.getSystemService()} with the argument
  * {@link android.content.Context#SENSOR_SERVICE}.
+ * </p>
+ * <p>
+ * Always make sure to disable sensors you don't need, especially when your
+ * activity is paused. Failing to do so can drain the battery in just a few
+ * hours. Note that the system will <i>not</i> disable sensors automatically when
+ * the screen turns off.
+ * </p>
  *
  * <pre class="prettyprint">
  * public class SensorActivity extends Activity, implements SensorEventListener {
@@ -48,13 +56,22 @@
  *     public SensorActivity() {
  *         mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
  *         mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ *     }
+ *
+ *     protected void onResume() {
+ *         super.onResume();
  *         mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
  *     }
  *
+ *     protected void onPause() {
+ *         super.onPause();
+ *         mSensorManager.unregisterListener(this);
+ *     }
+ *
  *     public void onAccuracyChanged(Sensor sensor, int accuracy) {
  *     }
  *
- *     public abstract void onSensorChanged(SensorEvent event) {
+ *     public void onSensorChanged(SensorEvent event) {
  *     }
  * }
  * </pre>
@@ -1769,6 +1786,183 @@
     }
 
 
+    /** Helper function to compute the angle change between two rotation matrices.
+     *  Given a current rotation matrix (R) and a previous rotation matrix
+     *  (prevR) computes the rotation around the x,y, and z axes which
+     *  transforms prevR to R.
+     *  outputs a 3 element vector containing the x,y, and z angle
+     *  change at indexes 0, 1, and 2 respectively.
+     * <p> Each input matrix is either as a 3x3 or 4x4 row-major matrix
+     * depending on the length of the passed array:
+     * <p>If the array length is 9, then the array elements represent this matrix
+     * <pre>
+     *   /  R[ 0]   R[ 1]   R[ 2]   \
+     *   |  R[ 3]   R[ 4]   R[ 5]   |
+     *   \  R[ 6]   R[ 7]   R[ 8]   /
+     *</pre>
+     * <p>If the array length is 16, then the array elements represent this matrix
+     * <pre>
+     *   /  R[ 0]   R[ 1]   R[ 2]   R[ 3]  \
+     *   |  R[ 4]   R[ 5]   R[ 6]   R[ 7]  |
+     *   |  R[ 8]   R[ 9]   R[10]   R[11]  |
+     *   \  R[12]   R[13]   R[14]   R[15]  /
+     *</pre>
+     * @param R current rotation matrix
+     * @param prevR previous rotation matrix
+     * @param angleChange an array of floats in which the angle change is stored
+     */
+
+    public static void getAngleChange( float[] angleChange, float[] R, float[] prevR) {
+        float rd1=0,rd4=0, rd6=0,rd7=0, rd8=0;
+        float ri0=0,ri1=0,ri2=0,ri3=0,ri4=0,ri5=0,ri6=0,ri7=0,ri8=0;
+        float pri0=0, pri1=0, pri2=0, pri3=0, pri4=0, pri5=0, pri6=0, pri7=0, pri8=0;
+        int i, j, k;
+
+        if(R.length == 9) {
+            ri0 = R[0];
+            ri1 = R[1];
+            ri2 = R[2];
+            ri3 = R[3];
+            ri4 = R[4];
+            ri5 = R[5];
+            ri6 = R[6];
+            ri7 = R[7];
+            ri8 = R[8];
+        } else if(R.length == 16) {
+            ri0 = R[0];
+            ri1 = R[1];
+            ri2 = R[2];
+            ri3 = R[4];
+            ri4 = R[5];
+            ri5 = R[6];
+            ri6 = R[8];
+            ri7 = R[9];
+            ri8 = R[10];
+        }
+
+        if(prevR.length == 9) {
+            pri0 = R[0];
+            pri1 = R[1];
+            pri2 = R[2];
+            pri3 = R[3];
+            pri4 = R[4];
+            pri5 = R[5];
+            pri6 = R[6];
+            pri7 = R[7];
+            pri8 = R[8];
+        } else if(prevR.length == 16) {
+            pri0 = R[0];
+            pri1 = R[1];
+            pri2 = R[2];
+            pri3 = R[4];
+            pri4 = R[5];
+            pri5 = R[6];
+            pri6 = R[8];
+            pri7 = R[9];
+            pri8 = R[10];
+        }
+
+        // calculate the parts of the rotation difference matrix we need
+        // rd[i][j] = pri[0][i] * ri[0][j] + pri[1][i] * ri[1][j] + pri[2][i] * ri[2][j];
+
+        rd1 = pri0 * ri1 + pri3 * ri4 + pri6 * ri7; //rd[0][1]
+        rd4 = pri1 * ri1 + pri4 * ri4 + pri7 * ri7; //rd[1][1]
+        rd6 = pri2 * ri0 + pri5 * ri3 + pri8 * ri6; //rd[2][0]
+        rd7 = pri2 * ri1 + pri5 * ri4 + pri8 * ri7; //rd[2][1]
+        rd8 = pri2 * ri2 + pri5 * ri5 + pri8 * ri8; //rd[2][2]
+
+        angleChange[0] = (float)Math.atan2(rd1, rd4);
+        angleChange[1] = (float)Math.asin(-rd7);
+        angleChange[2] = (float)Math.atan2(-rd6, rd8);
+
+    }
+
+    /** Helper function to convert a rotation vector to a rotation matrix.
+     *  Given a rotation vector (presumably from a ROTATION_VECTOR sensor), returns a
+     *  9  or 16 element rotation matrix in the array R.  R must have length 9 or 16.
+     *  If R.length == 9, the following matrix is returned:
+     * <pre>
+     *   /  R[ 0]   R[ 1]   R[ 2]   \
+     *   |  R[ 3]   R[ 4]   R[ 5]   |
+     *   \  R[ 6]   R[ 7]   R[ 8]   /
+     *</pre>
+     * If R.length == 16, the following matrix is returned:
+     * <pre>
+     *   /  R[ 0]   R[ 1]   R[ 2]   0  \
+     *   |  R[ 4]   R[ 5]   R[ 6]   0  |
+     *   |  R[ 8]   R[ 9]   R[10]   0  |
+     *   \  0       0       0       1  /
+     *</pre>
+     *  @param rotationVector the rotation vector to convert
+     *  @param R an array of floats in which to store the rotation matrix
+     */
+    public static void getRotationMatrixFromVector(float[] R, float[] rotationVector) {
+        float q0 = (float)Math.sqrt(1 - rotationVector[0]*rotationVector[0] -
+                                    rotationVector[1]*rotationVector[1] -
+                                    rotationVector[2]*rotationVector[2]);
+        float q1 = rotationVector[0];
+        float q2 = rotationVector[1];
+        float q3 = rotationVector[2];
+
+        float sq_q1 = 2 * q1 * q1;
+        float sq_q2 = 2 * q2 * q2;
+        float sq_q3 = 2 * q3 * q3;
+        float q1_q2 = 2 * q1 * q2;
+        float q3_q0 = 2 * q3 * q0;
+        float q1_q3 = 2 * q1 * q3;
+        float q2_q0 = 2 * q2 * q0;
+        float q2_q3 = 2 * q2 * q3;
+        float q1_q0 = 2 * q1 * q0;
+
+        if(R.length == 9) {
+            R[0] = 1 - sq_q2 - sq_q3;
+            R[1] = q1_q2 - q3_q0;
+            R[2] = q1_q3 + q2_q0;
+
+            R[3] = q1_q2 + q3_q0;
+            R[4] = 1 - sq_q1 - sq_q3;
+            R[5] = q2_q3 - q1_q0;
+
+            R[6] = q1_q3 - q2_q0;
+            R[7] = q2_q3 + q1_q0;
+            R[8] = 1 - sq_q1 - sq_q2;
+        } else if (R.length == 16) {
+            R[0] = 1 - sq_q2 - sq_q3;
+            R[1] = q1_q2 - q3_q0;
+            R[2] = q1_q3 + q2_q0;
+            R[3] = 0.0f;
+
+            R[4] = q1_q2 + q3_q0;
+            R[5] = 1 - sq_q1 - sq_q3;
+            R[6] = q2_q3 - q1_q0;
+            R[7] = 0.0f;
+
+            R[8] = q1_q3 - q2_q0;
+            R[9] = q2_q3 + q1_q0;
+            R[10] = 1 - sq_q1 - sq_q2;
+            R[11] = 0.0f;
+
+            R[12] = R[13] = R[14] = 0.0f;
+            R[15] = 1.0f;
+        }
+    }
+
+    /** Helper function to convert a rotation vector to a normalized quaternion.
+     *  Given a rotation vector (presumably from a ROTATION_VECTOR sensor), returns a normalized
+     *  quaternion in the array Q.  The quaternion is stored as [w, x, y, z]
+     *  @param rv the rotation vector to convert
+     *  @param Q an array of floats in which to store the computed quaternion
+     */
+    public static void getQuaternionFromVector(float[] Q, float[] rv) {
+        float w = (float)Math.sqrt(1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]);
+        //In this case, the w component of the quaternion is known to be a positive number
+
+        Q[0] = w;
+        Q[1] = rv[0];
+        Q[2] = rv[1];
+        Q[3] = rv[2];
+    }
+
     private static native void nativeClassInit();
 
     private static native int sensors_module_init();
diff --git a/core/java/android/net/NetworkProperties.java b/core/java/android/net/NetworkProperties.java
index da2a2d4..03c0a2e 100644
--- a/core/java/android/net/NetworkProperties.java
+++ b/core/java/android/net/NetworkProperties.java
@@ -99,19 +99,20 @@
         return 0;
     }
 
+    @Override
     public synchronized String toString() {
         String ifaceName = (mIface == null ? "" : "InterfaceName: " + mIface.getName() + " ");
 
         String ip = "IpAddresses: [";
-        for (InetAddress addr : mAddresses) ip +=  addr.toString() + ",";
+        for (InetAddress addr : mAddresses) ip +=  addr.getHostAddress() + ",";
         ip += "] ";
 
         String dns = "DnsAddresses: [";
-        for (InetAddress addr : mDnses) dns += addr.toString() + ",";
+        for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
         dns += "] ";
 
         String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
-        String gateway = (mGateway == null ? "" : "Gateway: " + mGateway.toString() + " ");
+        String gateway = (mGateway == null ? "" : "Gateway: " + mGateway.getHostAddress() + " ");
 
         return ifaceName + ip + gateway + dns + proxy;
     }
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
index 6828dd4..207fb51 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyProperties.java
@@ -57,6 +57,14 @@
         mExclusionList = exclusionList;
     }
 
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(mProxy.getHostAddress()).append(":").append(mPort)
+          .append(" xl=").append(mExclusionList);
+        return sb.toString();
+    }
+
     /**
      * Implement the Parcelable interface
      * @hide
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 63adcd0..3b21590 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -28,8 +28,10 @@
 import java.util.AbstractList;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.RandomAccess;
+import java.util.Set;
 
 /**
  * Immutable URI reference. A URI reference includes a URI and a fragment, the
@@ -47,7 +49,7 @@
     /*
 
     This class aims to do as little up front work as possible. To accomplish
-    that, we vary the implementation dependending on what the user passes in.
+    that, we vary the implementation depending on what the user passes in.
     For example, we have one implementation if the user passes in a
     URI string (StringUri) and another if the user passes in the
     individual components (OpaqueUri).
@@ -1253,14 +1255,16 @@
      * concurrent use.
      *
      * <p>An absolute hierarchical URI reference follows the pattern:
-     * {@code &lt;scheme&gt;://&lt;authority&gt;&lt;absolute path&gt;?&lt;query&gt;#&lt;fragment&gt;}
+     * {@code <scheme>://<authority><absolute path>?<query>#<fragment>}
      *
      * <p>Relative URI references (which are always hierarchical) follow one
-     * of two patterns: {@code &lt;relative or absolute path&gt;?&lt;query&gt;#&lt;fragment&gt;}
-     * or {@code //&lt;authority&gt;&lt;absolute path&gt;?&lt;query&gt;#&lt;fragment&gt;}
+     * of two patterns: {@code <relative or absolute path>?<query>#<fragment>}
+     * or {@code //<authority><absolute path>?<query>#<fragment>}
      *
      * <p>An opaque URI follows this pattern:
-     * {@code &lt;scheme&gt;:&lt;opaque part&gt;#&lt;fragment&gt;}
+     * {@code <scheme>:<opaque part>#<fragment>}
+     * 
+     * <p>Use {@link Uri#buildUpon()} to obtain a builder representing an existing URI.
      */
     public static final class Builder {
 
@@ -1447,6 +1451,13 @@
         }
 
         /**
+         * Clears the the previously set query.
+         */
+        public Builder clearQuery() {
+          return query((Part) null);
+        }
+
+        /**
          * Constructs a Uri with the current attributes.
          *
          * @throws UnsupportedOperationException if the URI is opaque and the
@@ -1491,19 +1502,60 @@
     }
 
     /**
+     * Returns a set of the unique names of all query parameters. Iterating
+     * over the set will return the names in order of their first occurrence.
+     *
+     * @throws UnsupportedOperationException if this isn't a hierarchical URI
+     *
+     * @return a set of decoded names
+     */
+    public Set<String> getQueryParameterNames() {
+        if (isOpaque()) {
+            throw new UnsupportedOperationException(NOT_HIERARCHICAL);
+        }
+
+        String query = getEncodedQuery();
+        if (query == null) {
+            return Collections.emptySet();
+        }
+
+        Set<String> names = new LinkedHashSet<String>();
+        int start = 0;
+        do {
+            int next = query.indexOf('&', start);
+            int end = (next == -1) ? query.length() : next;
+
+            int separator = query.indexOf('=', start);
+            if (separator > end || separator == -1) {
+                separator = end;
+            }
+
+            String name = query.substring(start, separator);
+            names.add(decode(name));
+
+            // Move start to end of name.
+            start = end + 1;
+        } while (start < query.length());
+
+        return Collections.unmodifiableSet(names);
+    }
+
+    /**
      * Searches the query string for parameter values with the given key.
      *
      * @param key which will be encoded
      *
      * @throws UnsupportedOperationException if this isn't a hierarchical URI
      * @throws NullPointerException if key is null
-     *
      * @return a list of decoded values
      */
     public List<String> getQueryParameters(String key) {
         if (isOpaque()) {
             throw new UnsupportedOperationException(NOT_HIERARCHICAL);
         }
+        if (key == null) {
+          throw new NullPointerException("key");
+        }
 
         String query = getEncodedQuery();
         if (query == null) {
@@ -1517,39 +1569,34 @@
             throw new AssertionError(e);
         }
 
-        // Prepend query with "&" making the first parameter the same as the
-        // rest.
-        query = "&" + query;
-
-        // Parameter prefix.
-        String prefix = "&" + encodedKey + "=";
-
         ArrayList<String> values = new ArrayList<String>();
 
         int start = 0;
-        int length = query.length();
-        while (start < length) {
-            start = query.indexOf(prefix, start);
+        do {
+            int nextAmpersand = query.indexOf('&', start);
+            int end = nextAmpersand != -1 ? nextAmpersand : query.length();
 
-            if (start == -1) {
-                // No more values.
+            int separator = query.indexOf('=', start);
+            if (separator > end || separator == -1) {
+                separator = end;
+            }
+
+            if (separator - start == encodedKey.length()
+                    && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
+                if (separator == end) {
+                  values.add("");
+                } else {
+                  values.add(decode(query.substring(separator + 1, end)));
+                }
+            }
+
+            // Move start to end of name.
+            if (nextAmpersand != -1) {
+                start = nextAmpersand + 1;
+            } else {
                 break;
             }
-
-            // Move start to start of value.
-            start += prefix.length();
-
-            // Find end of value.
-            int end = query.indexOf('&', start);
-            if (end == -1) {
-                end = query.length();
-            }
-
-            String value = query.substring(start, end);
-            values.add(decode(value));
-
-            start = end;
-        }
+        } while (true);
 
         return Collections.unmodifiableList(values);
     }
@@ -1560,7 +1607,6 @@
      * @param key which will be encoded
      * @throws UnsupportedOperationException if this isn't a hierarchical URI
      * @throws NullPointerException if key is null
-     *
      * @return the decoded value or null if no parameter is found
      */
     public String getQueryParameter(String key) {
@@ -1577,34 +1623,33 @@
         }
 
         final String encodedKey = encode(key, null);
-        final int encodedKeyLength = encodedKey.length();
+        final int length = query.length();
+        int start = 0;
+        do {
+            int nextAmpersand = query.indexOf('&', start);
+            int end = nextAmpersand != -1 ? nextAmpersand : length;
 
-        int encodedKeySearchIndex = 0;
-        final int encodedKeySearchEnd = query.length() - (encodedKeyLength + 1);
+            int separator = query.indexOf('=', start);
+            if (separator > end || separator == -1) {
+                separator = end;
+            }
 
-        while (encodedKeySearchIndex <= encodedKeySearchEnd) {
-            int keyIndex = query.indexOf(encodedKey, encodedKeySearchIndex);
-            if (keyIndex == -1) {
-                break;
-            }
-            final int equalsIndex = keyIndex + encodedKeyLength;
-            if (equalsIndex >= query.length()) {
-                break;
-            }
-            if (query.charAt(equalsIndex) != '=') {
-                encodedKeySearchIndex = equalsIndex + 1;
-                continue;
-            }
-            if (keyIndex == 0 || query.charAt(keyIndex - 1) == '&') {
-                int end = query.indexOf('&', equalsIndex);
-                if (end == -1) {
-                    end = query.length();
+            if (separator - start == encodedKey.length()
+                    && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
+                if (separator == end) {
+                  return "";
+                } else {
+                  return decode(query.substring(separator + 1, end));
                 }
-                return decode(query.substring(equalsIndex + 1, end));
-            } else {
-                encodedKeySearchIndex = equalsIndex + 1;
             }
-        }
+
+            // Move start to end of name.
+            if (nextAmpersand != -1) {
+                start = nextAmpersand + 1;
+            } else {
+                break;
+            }
+        } while (true);
         return null;
     }
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 95f217f..9fe6e01 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1400,6 +1400,7 @@
                         pw.println(getDischargeStartLevel());
                 pw.print(prefix); pw.print("    Discharge cycle current level: ");
                         pw.println(getDischargeCurrentLevel());
+            } else {
                 pw.print(prefix); pw.println("  Device is currently plugged into power");
                 pw.print(prefix); pw.print("    Last discharge cycle start level: "); 
                         pw.println(getDischargeStartLevel());
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 8624467..9268cd1 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -206,7 +206,8 @@
          * <p>Applications targeting this or a later release will get these
          * new changes in behavior:</p>
          * <ul>
-         * <li> Alerts UI change?
+         * <li> The default theme for applications is now dark holographic:
+         *      {@link android.R.style#Theme_Holo}.
          * </ul>
          */
         public static final int HONEYCOMB = CUR_DEVELOPMENT;
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 49b72fe..eb941e4 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -73,7 +73,18 @@
      * receiver.
      */
     public Messenger replyTo;
-    
+
+    /** If set message is in use */
+    /*package*/ static final int FLAG_IN_USE = 1;
+
+    /** Flags reserved for future use (All are reserved for now) */
+    /*package*/ static final int FLAGS_RESERVED = ~FLAG_IN_USE;
+
+    /** Flags to clear in the copyFrom method */
+    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAGS_RESERVED | FLAG_IN_USE;
+
+    /*package*/ int flags;
+
     /*package*/ long when;
     
     /*package*/ Bundle data;
@@ -253,6 +264,7 @@
      * target/callback of the original message.
      */
     public void copyFrom(Message o) {
+        this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
         this.what = o.what;
         this.arg1 = o.arg1;
         this.arg2 = o.arg2;
@@ -350,6 +362,7 @@
     }
 
     /*package*/ void clearForRecycle() {
+        flags = 0;
         what = 0;
         arg1 = 0;
         arg2 = 0;
@@ -361,6 +374,14 @@
         data = null;
     }
 
+    /*package*/ boolean isInUse() {
+        return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
+    }
+
+    /*package*/ void markInUse() {
+        flags |= FLAG_IN_USE;
+    }
+
     /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
     */
     public Message() {
@@ -453,4 +474,3 @@
         replyTo = Messenger.readMessengerOrNullFromParcel(source);
     }
 }
-
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index adb11c8..75dfdd2 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -120,6 +120,7 @@
                 now = SystemClock.uptimeMillis();
                 Message msg = pullNextLocked(now);
                 if (msg != null) {
+                    msg.markInUse();
                     return msg;
                 }
                 
@@ -192,7 +193,7 @@
     }
 
     final boolean enqueueMessage(Message msg, long when) {
-        if (msg.when != 0) {
+        if (msg.isInUse()) {
             throw new AndroidRuntimeException(msg
                     + " This message is already in use.");
         }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index ac12e10..7f7b02b 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -114,7 +114,7 @@
      *
      * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
      */
-    public static void setThreadBlockingPolicy(final int policyMask) {
+    public static void setThreadPolicy(final int policyMask) {
         // In addition to the Java-level thread-local in Dalvik's
         // BlockGuard, we also need to keep a native thread-local in
         // Binder in order to propagate the value across Binder calls,
@@ -164,11 +164,43 @@
      *
      * @return the bitmask of all the DISALLOW_* and PENALTY_* bits currently enabled
      */
-    public static int getThreadBlockingPolicy() {
+    public static int getThreadPolicy() {
         return BlockGuard.getThreadPolicy().getPolicyMask();
     }
 
     /**
+     * Updates the current thread's policy mask to allow reading &amp;
+     * writing to disk.
+     *
+     * @return the old policy mask, to be passed to setThreadPolicy to
+     *         restore the policy.
+     */
+    public static int allowThreadDiskWrites() {
+        int oldPolicy = getThreadPolicy();
+        int newPolicy = oldPolicy & ~(DISALLOW_DISK_WRITE | DISALLOW_DISK_READ);
+        if (newPolicy != oldPolicy) {
+            setThreadPolicy(newPolicy);
+        }
+        return oldPolicy;
+    }
+
+    /**
+     * Updates the current thread's policy mask to allow reading from
+     * disk.
+     *
+     * @return the old policy mask, to be passed to setThreadPolicy to
+     *         restore the policy.
+     */
+    public static int allowThreadDiskReads() {
+        int oldPolicy = getThreadPolicy();
+        int newPolicy = oldPolicy & ~(DISALLOW_DISK_READ);
+        if (newPolicy != oldPolicy) {
+            setThreadPolicy(newPolicy);
+        }
+        return oldPolicy;
+    }
+
+    /**
      * Parses the BlockGuard policy mask out from the Exception's
      * getMessage() String value.  Kinda gross, but least
      * invasive.  :/
@@ -413,13 +445,13 @@
             if (violationMaskSubset != 0) {
                 int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage);
                 violationMaskSubset |= violationBit;
-                final int savedPolicy = getThreadBlockingPolicy();
+                final int savedPolicy = getThreadPolicy();
                 try {
                     // First, remove any policy before we call into the Activity Manager,
                     // otherwise we'll infinite recurse as we try to log policy violations
                     // to disk, thus violating policy, thus requiring logging, etc...
                     // We restore the current policy below, in the finally block.
-                    setThreadBlockingPolicy(0);
+                    setThreadPolicy(0);
 
                     ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
                         RuntimeInit.getApplicationObject(),
@@ -429,7 +461,7 @@
                     Log.e(TAG, "RemoteException trying to handle StrictMode violation", e);
                 } finally {
                     // Restore the policy.
-                    setThreadBlockingPolicy(savedPolicy);
+                    setThreadPolicy(savedPolicy);
                 }
             }
 
@@ -487,7 +519,7 @@
         new LogStackTrace().printStackTrace(new PrintWriter(sw));
         String ourStack = sw.toString();
 
-        int policyMask = getThreadBlockingPolicy();
+        int policyMask = getThreadPolicy();
         boolean currentlyGathering = (policyMask & PENALTY_GATHER) != 0;
 
         int numViolations = p.readInt();
diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
deleted file mode 100644
index 5c69214..0000000
--- a/core/java/android/os/storage/IMountService.aidl
+++ /dev/null
@@ -1,184 +0,0 @@
-/* //device/java/android/android/os/IUsb.aidl
-**
-** 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.
-*/
-
-package android.os.storage;
-
-import android.os.storage.IMountServiceListener;
-import android.os.storage.IMountShutdownObserver;
-import android.os.storage.IObbActionListener;
-
-/** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
- * In particular, the ordering of the methods below must match the 
- * _TRANSACTION enum in IMountService.cpp
- * @hide - Applications should use android.os.storage.StorageManager to access
- * storage functions.
- */
-interface IMountService
-{
-    /**
-     * Registers an IMountServiceListener for receiving async
-     * notifications.
-     */
-    void registerListener(IMountServiceListener listener);
-
-    /**
-     * Unregisters an IMountServiceListener
-     */
-    void unregisterListener(IMountServiceListener listener);
-
-    /**
-     * Returns true if a USB mass storage host is connected
-     */
-    boolean isUsbMassStorageConnected();
-
-    /**
-     * Enables / disables USB mass storage.
-     * The caller should check actual status of enabling/disabling
-     * USB mass storage via StorageEventListener.
-     */
-    void setUsbMassStorageEnabled(boolean enable);
-
-    /**
-     * Returns true if a USB mass storage host is enabled (media is shared)
-     */
-    boolean isUsbMassStorageEnabled();
-
-    /**
-     * Mount external storage at given mount point.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int mountVolume(String mountPoint);
-
-    /**
-     * Safely unmount external storage at given mount point.
-     * The unmount is an asynchronous operation. Applications
-     * should register StorageEventListener for storage related
-     * status changes.
-     * 
-     */
-    void unmountVolume(String mountPoint, boolean force);
-
-    /**
-     * Format external storage given a mount point.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int formatVolume(String mountPoint);
-
-    /**
-     * Returns an array of pids with open files on
-     * the specified path.
-     */
-    int[] getStorageUsers(String path);
-
-    /**
-     * Gets the state of a volume via its mountpoint.
-     */
-    String getVolumeState(String mountPoint);
-
-    /*
-     * Creates a secure container with the specified parameters.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid);
-
-    /*
-     * Finalize a container which has just been created and populated.
-     * After finalization, the container is immutable.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int finalizeSecureContainer(String id);
-
-    /*
-     * Destroy a secure container, and free up all resources associated with it.
-     * NOTE: Ensure all references are released prior to deleting.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int destroySecureContainer(String id, boolean force);
-
-    /*
-     * Mount a secure container with the specified key and owner UID.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int mountSecureContainer(String id, String key, int ownerUid);
-
-    /*
-     * Unount a secure container.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int unmountSecureContainer(String id, boolean force);
-
-    /*
-     * Returns true if the specified container is mounted
-     */
-    boolean isSecureContainerMounted(String id);
-
-    /*
-     * Rename an unmounted secure container.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int renameSecureContainer(String oldId, String newId);
-
-    /*
-     * Returns the filesystem path of a mounted secure container.
-     */
-    String getSecureContainerPath(String id);
-
-    /**
-     * Gets an Array of currently known secure container IDs
-     */
-    String[] getSecureContainerList();
-
-    /**
-     * Shuts down the MountService and gracefully unmounts all external media.
-     * Invokes call back once the shutdown is complete.
-     */
-    void shutdown(IMountShutdownObserver observer);
-
-    /**
-     * Call into MountService by PackageManager to notify that its done
-     * processing the media status update request.
-     */
-    void finishMediaUpdate();
-
-    /**
-     * Mounts an Opaque Binary Blob (OBB) with the specified decryption key and only
-     * allows the calling process's UID access to the contents.
-     *
-     * MountService will call back to the supplied IObbActionListener to inform
-     * it of the terminal state of the call.
-     */
-    void mountObb(String filename, String key, IObbActionListener token);
-
-    /**
-     * Unmounts an Opaque Binary Blob (OBB). When the force flag is specified, any
-     * program using it will be forcibly killed to unmount the image.
-     *
-     * MountService will call back to the supplied IObbActionListener to inform
-     * it of the terminal state of the call.
-     */
-    void unmountObb(String filename, boolean force, IObbActionListener token);
-
-    /**
-     * Checks whether the specified Opaque Binary Blob (OBB) is mounted somewhere.
-     */
-    boolean isObbMounted(String filename);
-
-    /**
-     * Gets the path to the mounted Opaque Binary Blob (OBB).
-     */
-    String getMountedObbPath(String filename);
-}
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
new file mode 100644
index 0000000..60ea95c
--- /dev/null
+++ b/core/java/android/os/storage/IMountService.java
@@ -0,0 +1,1046 @@
+/*
+ * 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.os.storage;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+/**
+ * WARNING! Update IMountService.h and IMountService.cpp if you change this
+ * file. In particular, the ordering of the methods below must match the
+ * _TRANSACTION enum in IMountService.cpp
+ * 
+ * @hide - Applications should use android.os.storage.StorageManager to access
+ *       storage functions.
+ */
+public interface IMountService extends IInterface {
+    /** Local-side IPC implementation stub class. */
+    public static abstract class Stub extends Binder implements IMountService {
+        private static class Proxy implements IMountService {
+            private IBinder mRemote;
+
+            Proxy(IBinder remote) {
+                mRemote = remote;
+            }
+
+            public IBinder asBinder() {
+                return mRemote;
+            }
+
+            public String getInterfaceDescriptor() {
+                return DESCRIPTOR;
+            }
+
+            /**
+             * Registers an IMountServiceListener for receiving async
+             * notifications.
+             */
+            public void registerListener(IMountServiceListener listener) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeStrongBinder((listener != null ? listener.asBinder() : null));
+                    mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Unregisters an IMountServiceListener
+             */
+            public void unregisterListener(IMountServiceListener listener) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeStrongBinder((listener != null ? listener.asBinder() : null));
+                    mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Returns true if a USB mass storage host is connected
+             */
+            public boolean isUsbMassStorageConnected() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_isUsbMassStorageConnected, _data, _reply, 0);
+                    _reply.readException();
+                    _result = 0 != _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Enables / disables USB mass storage. The caller should check
+             * actual status of enabling/disabling USB mass storage via
+             * StorageEventListener.
+             */
+            public void setUsbMassStorageEnabled(boolean enable) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt((enable ? 1 : 0));
+                    mRemote.transact(Stub.TRANSACTION_setUsbMassStorageEnabled, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Returns true if a USB mass storage host is enabled (media is
+             * shared)
+             */
+            public boolean isUsbMassStorageEnabled() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_isUsbMassStorageEnabled, _data, _reply, 0);
+                    _reply.readException();
+                    _result = 0 != _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Mount external storage at given mount point. Returns an int
+             * consistent with MountServiceResultCode
+             */
+            public int mountVolume(String mountPoint) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(mountPoint);
+                    mRemote.transact(Stub.TRANSACTION_mountVolume, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Safely unmount external storage at given mount point. The unmount
+             * is an asynchronous operation. Applications should register
+             * StorageEventListener for storage related status changes.
+             */
+            public void unmountVolume(String mountPoint, boolean force) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(mountPoint);
+                    _data.writeInt((force ? 1 : 0));
+                    mRemote.transact(Stub.TRANSACTION_unmountVolume, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Format external storage given a mount point. Returns an int
+             * consistent with MountServiceResultCode
+             */
+            public int formatVolume(String mountPoint) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(mountPoint);
+                    mRemote.transact(Stub.TRANSACTION_formatVolume, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Returns an array of pids with open files on the specified path.
+             */
+            public int[] getStorageUsers(String path) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int[] _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(path);
+                    mRemote.transact(Stub.TRANSACTION_getStorageUsers, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.createIntArray();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Gets the state of a volume via its mountpoint.
+             */
+            public String getVolumeState(String mountPoint) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(mountPoint);
+                    mRemote.transact(Stub.TRANSACTION_getVolumeState, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readString();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Creates a secure container with the specified parameters. Returns
+             * an int consistent with MountServiceResultCode
+             */
+            public int createSecureContainer(String id, int sizeMb, String fstype, String key,
+                    int ownerUid) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    _data.writeInt(sizeMb);
+                    _data.writeString(fstype);
+                    _data.writeString(key);
+                    _data.writeInt(ownerUid);
+                    mRemote.transact(Stub.TRANSACTION_createSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Destroy a secure container, and free up all resources associated
+             * with it. NOTE: Ensure all references are released prior to
+             * deleting. Returns an int consistent with MountServiceResultCode
+             */
+            public int destroySecureContainer(String id, boolean force) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    _data.writeInt((force ? 1 : 0));
+                    mRemote.transact(Stub.TRANSACTION_destroySecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Finalize a container which has just been created and populated.
+             * After finalization, the container is immutable. Returns an int
+             * consistent with MountServiceResultCode
+             */
+            public int finalizeSecureContainer(String id) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    mRemote.transact(Stub.TRANSACTION_finalizeSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Mount a secure container with the specified key and owner UID.
+             * Returns an int consistent with MountServiceResultCode
+             */
+            public int mountSecureContainer(String id, String key, int ownerUid)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    _data.writeString(key);
+                    _data.writeInt(ownerUid);
+                    mRemote.transact(Stub.TRANSACTION_mountSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Unount a secure container. Returns an int consistent with
+             * MountServiceResultCode
+             */
+            public int unmountSecureContainer(String id, boolean force) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    _data.writeInt((force ? 1 : 0));
+                    mRemote.transact(Stub.TRANSACTION_unmountSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Returns true if the specified container is mounted
+             */
+            public boolean isSecureContainerMounted(String id) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    mRemote.transact(Stub.TRANSACTION_isSecureContainerMounted, _data, _reply, 0);
+                    _reply.readException();
+                    _result = 0 != _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Rename an unmounted secure container. Returns an int consistent
+             * with MountServiceResultCode
+             */
+            public int renameSecureContainer(String oldId, String newId) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(oldId);
+                    _data.writeString(newId);
+                    mRemote.transact(Stub.TRANSACTION_renameSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Returns the filesystem path of a mounted secure container.
+             */
+            public String getSecureContainerPath(String id) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    mRemote.transact(Stub.TRANSACTION_getSecureContainerPath, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readString();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Gets an Array of currently known secure container IDs
+             */
+            public String[] getSecureContainerList() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String[] _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_getSecureContainerList, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.createStringArray();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Shuts down the MountService and gracefully unmounts all external
+             * media. Invokes call back once the shutdown is complete.
+             */
+            public void shutdown(IMountShutdownObserver observer)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeStrongBinder((observer != null ? observer.asBinder() : null));
+                    mRemote.transact(Stub.TRANSACTION_shutdown, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Call into MountService by PackageManager to notify that its done
+             * processing the media status update request.
+             */
+            public void finishMediaUpdate() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_finishMediaUpdate, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Mounts an Opaque Binary Blob (OBB) with the specified decryption
+             * key and only allows the calling process's UID access to the
+             * contents. MountService will call back to the supplied
+             * IObbActionListener to inform it of the terminal state of the
+             * call.
+             */
+            public void mountObb(String filename, String key, IObbActionListener token)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(filename);
+                    _data.writeString(key);
+                    _data.writeStrongBinder((token != null ? token.asBinder() : null));
+                    mRemote.transact(Stub.TRANSACTION_mountObb, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Unmounts an Opaque Binary Blob (OBB). When the force flag is
+             * specified, any program using it will be forcibly killed to
+             * unmount the image. MountService will call back to the supplied
+             * IObbActionListener to inform it of the terminal state of the
+             * call.
+             */
+            public void unmountObb(String filename, boolean force, IObbActionListener token)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(filename);
+                    _data.writeInt((force ? 1 : 0));
+                    _data.writeStrongBinder((token != null ? token.asBinder() : null));
+                    mRemote.transact(Stub.TRANSACTION_unmountObb, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Checks whether the specified Opaque Binary Blob (OBB) is mounted
+             * somewhere.
+             */
+            public boolean isObbMounted(String filename) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(filename);
+                    mRemote.transact(Stub.TRANSACTION_isObbMounted, _data, _reply, 0);
+                    _reply.readException();
+                    _result = 0 != _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Gets the path to the mounted Opaque Binary Blob (OBB).
+             */
+            public String getMountedObbPath(String filename) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(filename);
+                    mRemote.transact(Stub.TRANSACTION_getMountedObbPath, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readString();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+        }
+
+        private static final String DESCRIPTOR = "IMountService";
+
+        static final int TRANSACTION_registerListener = IBinder.FIRST_CALL_TRANSACTION + 0;
+
+        static final int TRANSACTION_unregisterListener = IBinder.FIRST_CALL_TRANSACTION + 1;
+
+        static final int TRANSACTION_isUsbMassStorageConnected = IBinder.FIRST_CALL_TRANSACTION + 2;
+
+        static final int TRANSACTION_setUsbMassStorageEnabled = IBinder.FIRST_CALL_TRANSACTION + 3;
+
+        static final int TRANSACTION_isUsbMassStorageEnabled = IBinder.FIRST_CALL_TRANSACTION + 4;
+
+        static final int TRANSACTION_mountVolume = IBinder.FIRST_CALL_TRANSACTION + 5;
+
+        static final int TRANSACTION_unmountVolume = IBinder.FIRST_CALL_TRANSACTION + 6;
+
+        static final int TRANSACTION_formatVolume = IBinder.FIRST_CALL_TRANSACTION + 7;
+
+        static final int TRANSACTION_getStorageUsers = IBinder.FIRST_CALL_TRANSACTION + 8;
+
+        static final int TRANSACTION_getVolumeState = IBinder.FIRST_CALL_TRANSACTION + 9;
+
+        static final int TRANSACTION_createSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 10;
+
+        static final int TRANSACTION_finalizeSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 11;
+
+        static final int TRANSACTION_destroySecureContainer = IBinder.FIRST_CALL_TRANSACTION + 12;
+
+        static final int TRANSACTION_mountSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 13;
+
+        static final int TRANSACTION_unmountSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 14;
+
+        static final int TRANSACTION_isSecureContainerMounted = IBinder.FIRST_CALL_TRANSACTION + 15;
+
+        static final int TRANSACTION_renameSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 16;
+
+        static final int TRANSACTION_getSecureContainerPath = IBinder.FIRST_CALL_TRANSACTION + 17;
+
+        static final int TRANSACTION_getSecureContainerList = IBinder.FIRST_CALL_TRANSACTION + 18;
+
+        static final int TRANSACTION_shutdown = IBinder.FIRST_CALL_TRANSACTION + 19;
+
+        static final int TRANSACTION_finishMediaUpdate = IBinder.FIRST_CALL_TRANSACTION + 20;
+
+        static final int TRANSACTION_mountObb = IBinder.FIRST_CALL_TRANSACTION + 21;
+
+        static final int TRANSACTION_unmountObb = IBinder.FIRST_CALL_TRANSACTION + 22;
+
+        static final int TRANSACTION_isObbMounted = IBinder.FIRST_CALL_TRANSACTION + 23;
+
+        static final int TRANSACTION_getMountedObbPath = IBinder.FIRST_CALL_TRANSACTION + 24;
+
+        /**
+         * Cast an IBinder object into an IMountService interface, generating a
+         * proxy if needed.
+         */
+        public static IMountService asInterface(IBinder obj) {
+            if (obj == null) {
+                return null;
+            }
+            IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+            if (iin != null && iin instanceof IMountService) {
+                return (IMountService) iin;
+            }
+            return new IMountService.Stub.Proxy(obj);
+        }
+
+        /** Construct the stub at attach it to the interface. */
+        public Stub() {
+            attachInterface(this, DESCRIPTOR);
+        }
+
+        public IBinder asBinder() {
+            return this;
+        }
+
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply,
+                int flags) throws RemoteException {
+            switch (code) {
+                case INTERFACE_TRANSACTION: {
+                    reply.writeString(DESCRIPTOR);
+                    return true;
+                }
+                case TRANSACTION_registerListener: {
+                    data.enforceInterface(DESCRIPTOR);
+                    IMountServiceListener listener;
+                    listener = IMountServiceListener.Stub.asInterface(data.readStrongBinder());
+                    registerListener(listener);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_unregisterListener: {
+                    data.enforceInterface(DESCRIPTOR);
+                    IMountServiceListener listener;
+                    listener = IMountServiceListener.Stub.asInterface(data.readStrongBinder());
+                    unregisterListener(listener);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_isUsbMassStorageConnected: {
+                    data.enforceInterface(DESCRIPTOR);
+                    boolean result = isUsbMassStorageConnected();
+                    reply.writeNoException();
+                    reply.writeInt((result ? 1 : 0));
+                    return true;
+                }
+                case TRANSACTION_setUsbMassStorageEnabled: {
+                    data.enforceInterface(DESCRIPTOR);
+                    boolean enable;
+                    enable = 0 != data.readInt();
+                    setUsbMassStorageEnabled(enable);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_isUsbMassStorageEnabled: {
+                    data.enforceInterface(DESCRIPTOR);
+                    boolean result = isUsbMassStorageEnabled();
+                    reply.writeNoException();
+                    reply.writeInt((result ? 1 : 0));
+                    return true;
+                }
+                case TRANSACTION_mountVolume: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String mountPoint;
+                    mountPoint = data.readString();
+                    int resultCode = mountVolume(mountPoint);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_unmountVolume: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String mountPoint;
+                    mountPoint = data.readString();
+                    boolean force;
+                    force = 0 != data.readInt();
+                    unmountVolume(mountPoint, force);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_formatVolume: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String mountPoint;
+                    mountPoint = data.readString();
+                    int result = formatVolume(mountPoint);
+                    reply.writeNoException();
+                    reply.writeInt(result);
+                    return true;
+                }
+                case TRANSACTION_getStorageUsers: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String path;
+                    path = data.readString();
+                    int[] pids = getStorageUsers(path);
+                    reply.writeNoException();
+                    reply.writeIntArray(pids);
+                    return true;
+                }
+                case TRANSACTION_getVolumeState: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String mountPoint;
+                    mountPoint = data.readString();
+                    String state = getVolumeState(mountPoint);
+                    reply.writeNoException();
+                    reply.writeString(state);
+                    return true;
+                }
+                case TRANSACTION_createSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    int sizeMb;
+                    sizeMb = data.readInt();
+                    String fstype;
+                    fstype = data.readString();
+                    String key;
+                    key = data.readString();
+                    int ownerUid;
+                    ownerUid = data.readInt();
+                    int resultCode = createSecureContainer(id, sizeMb, fstype, key, ownerUid);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_finalizeSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    int resultCode = finalizeSecureContainer(id);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_destroySecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    boolean force;
+                    force = 0 != data.readInt();
+                    int resultCode = destroySecureContainer(id, force);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_mountSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    String key;
+                    key = data.readString();
+                    int ownerUid;
+                    ownerUid = data.readInt();
+                    int resultCode = mountSecureContainer(id, key, ownerUid);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_unmountSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    boolean force;
+                    force = 0 != data.readInt();
+                    int resultCode = unmountSecureContainer(id, force);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_isSecureContainerMounted: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    boolean status = isSecureContainerMounted(id);
+                    reply.writeNoException();
+                    reply.writeInt((status ? 1 : 0));
+                    return true;
+                }
+                case TRANSACTION_renameSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String oldId;
+                    oldId = data.readString();
+                    String newId;
+                    newId = data.readString();
+                    int resultCode = renameSecureContainer(oldId, newId);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_getSecureContainerPath: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    String path = getSecureContainerPath(id);
+                    reply.writeNoException();
+                    reply.writeString(path);
+                    return true;
+                }
+                case TRANSACTION_getSecureContainerList: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String[] ids = getSecureContainerList();
+                    reply.writeNoException();
+                    reply.writeStringArray(ids);
+                    return true;
+                }
+                case TRANSACTION_shutdown: {
+                    data.enforceInterface(DESCRIPTOR);
+                    IMountShutdownObserver observer;
+                    observer = IMountShutdownObserver.Stub.asInterface(data
+                            .readStrongBinder());
+                    shutdown(observer);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_finishMediaUpdate: {
+                    data.enforceInterface(DESCRIPTOR);
+                    finishMediaUpdate();
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_mountObb: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String filename;
+                    filename = data.readString();
+                    String key;
+                    key = data.readString();
+                    IObbActionListener observer;
+                    observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
+                    mountObb(filename, key, observer);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_unmountObb: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String filename;
+                    filename = data.readString();
+                    boolean force;
+                    force = 0 != data.readInt();
+                    IObbActionListener observer;
+                    observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
+                    unmountObb(filename, force, observer);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_isObbMounted: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String filename;
+                    filename = data.readString();
+                    boolean status = isObbMounted(filename);
+                    reply.writeNoException();
+                    reply.writeInt((status ? 1 : 0));
+                    return true;
+                }
+                case TRANSACTION_getMountedObbPath: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String filename;
+                    filename = data.readString();
+                    String mountedPath = getMountedObbPath(filename);
+                    reply.writeNoException();
+                    reply.writeString(mountedPath);
+                    return true;
+                }
+            }
+            return super.onTransact(code, data, reply, flags);
+        }
+    }
+
+    /*
+     * Creates a secure container with the specified parameters. Returns an int
+     * consistent with MountServiceResultCode
+     */
+    public int createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid)
+            throws RemoteException;
+
+    /*
+     * Destroy a secure container, and free up all resources associated with it.
+     * NOTE: Ensure all references are released prior to deleting. Returns an
+     * int consistent with MountServiceResultCode
+     */
+    public int destroySecureContainer(String id, boolean force) throws RemoteException;
+
+    /*
+     * Finalize a container which has just been created and populated. After
+     * finalization, the container is immutable. Returns an int consistent with
+     * MountServiceResultCode
+     */
+    public int finalizeSecureContainer(String id) throws RemoteException;
+
+    /**
+     * Call into MountService by PackageManager to notify that its done
+     * processing the media status update request.
+     */
+    public void finishMediaUpdate() throws RemoteException;
+
+    /**
+     * Format external storage given a mount point. Returns an int consistent
+     * with MountServiceResultCode
+     */
+    public int formatVolume(String mountPoint) throws RemoteException;
+
+    /**
+     * Gets the path to the mounted Opaque Binary Blob (OBB).
+     */
+    public String getMountedObbPath(String filename) throws RemoteException;
+
+    /**
+     * Gets an Array of currently known secure container IDs
+     */
+    public String[] getSecureContainerList() throws RemoteException;
+
+    /*
+     * Returns the filesystem path of a mounted secure container.
+     */
+    public String getSecureContainerPath(String id) throws RemoteException;
+
+    /**
+     * Returns an array of pids with open files on the specified path.
+     */
+    public int[] getStorageUsers(String path) throws RemoteException;
+
+    /**
+     * Gets the state of a volume via its mountpoint.
+     */
+    public String getVolumeState(String mountPoint) throws RemoteException;
+
+    /**
+     * Checks whether the specified Opaque Binary Blob (OBB) is mounted
+     * somewhere.
+     */
+    public boolean isObbMounted(String filename) throws RemoteException;
+
+    /*
+     * Returns true if the specified container is mounted
+     */
+    public boolean isSecureContainerMounted(String id) throws RemoteException;
+
+    /**
+     * Returns true if a USB mass storage host is connected
+     */
+    public boolean isUsbMassStorageConnected() throws RemoteException;
+
+    /**
+     * Returns true if a USB mass storage host is enabled (media is shared)
+     */
+    public boolean isUsbMassStorageEnabled() throws RemoteException;
+
+    /**
+     * Mounts an Opaque Binary Blob (OBB) with the specified decryption key and
+     * only allows the calling process's UID access to the contents.
+     * MountService will call back to the supplied IObbActionListener to inform
+     * it of the terminal state of the call.
+     */
+    public void mountObb(String filename, String key, IObbActionListener token)
+            throws RemoteException;
+
+    /*
+     * Mount a secure container with the specified key and owner UID. Returns an
+     * int consistent with MountServiceResultCode
+     */
+    public int mountSecureContainer(String id, String key, int ownerUid) throws RemoteException;
+
+    /**
+     * Mount external storage at given mount point. Returns an int consistent
+     * with MountServiceResultCode
+     */
+    public int mountVolume(String mountPoint) throws RemoteException;
+
+    /**
+     * Registers an IMountServiceListener for receiving async notifications.
+     */
+    public void registerListener(IMountServiceListener listener) throws RemoteException;
+
+    /*
+     * Rename an unmounted secure container. Returns an int consistent with
+     * MountServiceResultCode
+     */
+    public int renameSecureContainer(String oldId, String newId) throws RemoteException;
+
+    /**
+     * Enables / disables USB mass storage. The caller should check actual
+     * status of enabling/disabling USB mass storage via StorageEventListener.
+     */
+    public void setUsbMassStorageEnabled(boolean enable) throws RemoteException;
+
+    /**
+     * Shuts down the MountService and gracefully unmounts all external media.
+     * Invokes call back once the shutdown is complete.
+     */
+    public void shutdown(IMountShutdownObserver observer) throws RemoteException;
+
+    /**
+     * Unmounts an Opaque Binary Blob (OBB). When the force flag is specified,
+     * any program using it will be forcibly killed to unmount the image.
+     * MountService will call back to the supplied IObbActionListener to inform
+     * it of the terminal state of the call.
+     */
+    public void unmountObb(String filename, boolean force, IObbActionListener token)
+            throws RemoteException;
+
+    /*
+     * Unount a secure container. Returns an int consistent with
+     * MountServiceResultCode
+     */
+    public int unmountSecureContainer(String id, boolean force) throws RemoteException;
+
+    /**
+     * Safely unmount external storage at given mount point. The unmount is an
+     * asynchronous operation. Applications should register StorageEventListener
+     * for storage related status changes.
+     */
+    public void unmountVolume(String mountPoint, boolean force) throws RemoteException;
+
+    /**
+     * Unregisters an IMountServiceListener
+     */
+    public void unregisterListener(IMountServiceListener listener) throws RemoteException;
+}
diff --git a/core/java/android/os/storage/IMountServiceListener.aidl b/core/java/android/os/storage/IMountServiceListener.aidl
deleted file mode 100644
index 883413a..0000000
--- a/core/java/android/os/storage/IMountServiceListener.aidl
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os.storage;
-
-/**
- * Callback class for receiving events from MountService.
- *
- * @hide - Applications should use android.os.storage.IStorageEventListener
- * for storage event callbacks.
- */
-interface IMountServiceListener {
-    /**
-     * Detection state of USB Mass Storage has changed
-     *
-     * @param available true if a UMS host is connected.
-     */
-    void onUsbMassStorageConnectionChanged(boolean connected);
-
-    /**
-     * Storage state has changed.
-     *
-     * @param path The volume mount path.
-     * @param oldState The old state of the volume.
-     * @param newState The new state of the volume.
-     *
-     * Note: State is one of the values returned by Environment.getExternalStorageState()
-     */
-    void onStorageStateChanged(String path, String oldState, String newState);
-}
diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java
new file mode 100644
index 0000000..d5c5fa5
--- /dev/null
+++ b/core/java/android/os/storage/IMountServiceListener.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+/**
+ * Callback class for receiving events from MountService.
+ * 
+ * @hide - Applications should use IStorageEventListener for storage event
+ *       callbacks.
+ */
+public interface IMountServiceListener extends IInterface {
+    /** Local-side IPC implementation stub class. */
+    public static abstract class Stub extends Binder implements IMountServiceListener {
+        private static final String DESCRIPTOR = "IMountServiceListener";
+
+        /** Construct the stub at attach it to the interface. */
+        public Stub() {
+            this.attachInterface(this, DESCRIPTOR);
+        }
+
+        /**
+         * Cast an IBinder object into an IMountServiceListener interface,
+         * generating a proxy if needed.
+         */
+        public static IMountServiceListener asInterface(IBinder obj) {
+            if ((obj == null)) {
+                return null;
+            }
+            IInterface iin = (IInterface) obj.queryLocalInterface(DESCRIPTOR);
+            if (((iin != null) && (iin instanceof IMountServiceListener))) {
+                return ((IMountServiceListener) iin);
+            }
+            return new IMountServiceListener.Stub.Proxy(obj);
+        }
+
+        public IBinder asBinder() {
+            return this;
+        }
+
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            switch (code) {
+                case INTERFACE_TRANSACTION: {
+                    reply.writeString(DESCRIPTOR);
+                    return true;
+                }
+                case TRANSACTION_onUsbMassStorageConnectionChanged: {
+                    data.enforceInterface(DESCRIPTOR);
+                    boolean connected;
+                    connected = (0 != data.readInt());
+                    this.onUsbMassStorageConnectionChanged(connected);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_onStorageStateChanged: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String path;
+                    path = data.readString();
+                    String oldState;
+                    oldState = data.readString();
+                    String newState;
+                    newState = data.readString();
+                    this.onStorageStateChanged(path, oldState, newState);
+                    reply.writeNoException();
+                    return true;
+                }
+            }
+            return super.onTransact(code, data, reply, flags);
+        }
+
+        private static class Proxy implements IMountServiceListener {
+            private IBinder mRemote;
+
+            Proxy(IBinder remote) {
+                mRemote = remote;
+            }
+
+            public IBinder asBinder() {
+                return mRemote;
+            }
+
+            public String getInterfaceDescriptor() {
+                return DESCRIPTOR;
+            }
+
+            /**
+             * Detection state of USB Mass Storage has changed
+             * 
+             * @param available true if a UMS host is connected.
+             */
+            public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(((connected) ? (1) : (0)));
+                    mRemote.transact(Stub.TRANSACTION_onUsbMassStorageConnectionChanged, _data,
+                            _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Storage state has changed.
+             * 
+             * @param path The volume mount path.
+             * @param oldState The old state of the volume.
+             * @param newState The new state of the volume. Note: State is one
+             *            of the values returned by
+             *            Environment.getExternalStorageState()
+             */
+            public void onStorageStateChanged(String path, String oldState, String newState)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(path);
+                    _data.writeString(oldState);
+                    _data.writeString(newState);
+                    mRemote.transact(Stub.TRANSACTION_onStorageStateChanged, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+        }
+
+        static final int TRANSACTION_onUsbMassStorageConnectionChanged = (IBinder.FIRST_CALL_TRANSACTION + 0);
+
+        static final int TRANSACTION_onStorageStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 1);
+    }
+
+    /**
+     * Detection state of USB Mass Storage has changed
+     * 
+     * @param available true if a UMS host is connected.
+     */
+    public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException;
+
+    /**
+     * Storage state has changed.
+     * 
+     * @param path The volume mount path.
+     * @param oldState The old state of the volume.
+     * @param newState The new state of the volume. Note: State is one of the
+     *            values returned by Environment.getExternalStorageState()
+     */
+    public void onStorageStateChanged(String path, String oldState, String newState)
+            throws RemoteException;
+}
diff --git a/core/java/android/os/storage/IMountShutdownObserver.aidl b/core/java/android/os/storage/IMountShutdownObserver.aidl
deleted file mode 100644
index 0aa8a45..0000000
--- a/core/java/android/os/storage/IMountShutdownObserver.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os.storage;
-
-/**
- * Callback class for receiving events related
- * to shutdown.
- *
- * @hide - For internal consumption only.
- */
-interface IMountShutdownObserver {
-    /**
-     * This method is called when the shutdown
-     * of MountService completed.
-     * @param statusCode indicates success or failure
-     * of the shutdown.
-     */
-    void onShutDownComplete(int statusCode);
-}
diff --git a/core/java/android/os/storage/IMountShutdownObserver.java b/core/java/android/os/storage/IMountShutdownObserver.java
new file mode 100644
index 0000000..d946e1a
--- /dev/null
+++ b/core/java/android/os/storage/IMountShutdownObserver.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+/**
+ * Callback class for receiving events related to shutdown.
+ * 
+ * @hide - For internal consumption only.
+ */
+public interface IMountShutdownObserver extends IInterface {
+    /** Local-side IPC implementation stub class. */
+    public static abstract class Stub extends Binder implements IMountShutdownObserver {
+        private static final java.lang.String DESCRIPTOR = "IMountShutdownObserver";
+
+        /** Construct the stub at attach it to the interface. */
+        public Stub() {
+            this.attachInterface(this, DESCRIPTOR);
+        }
+
+        /**
+         * Cast an IBinder object into an IMountShutdownObserver interface,
+         * generating a proxy if needed.
+         */
+        public static IMountShutdownObserver asInterface(IBinder obj) {
+            if ((obj == null)) {
+                return null;
+            }
+            IInterface iin = (IInterface) obj.queryLocalInterface(DESCRIPTOR);
+            if (((iin != null) && (iin instanceof IMountShutdownObserver))) {
+                return ((IMountShutdownObserver) iin);
+            }
+            return new IMountShutdownObserver.Stub.Proxy(obj);
+        }
+
+        public IBinder asBinder() {
+            return this;
+        }
+
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            switch (code) {
+                case INTERFACE_TRANSACTION: {
+                    reply.writeString(DESCRIPTOR);
+                    return true;
+                }
+                case TRANSACTION_onShutDownComplete: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int statusCode;
+                    statusCode = data.readInt();
+                    this.onShutDownComplete(statusCode);
+                    reply.writeNoException();
+                    return true;
+                }
+            }
+            return super.onTransact(code, data, reply, flags);
+        }
+
+        private static class Proxy implements IMountShutdownObserver {
+            private IBinder mRemote;
+
+            Proxy(IBinder remote) {
+                mRemote = remote;
+            }
+
+            public IBinder asBinder() {
+                return mRemote;
+            }
+
+            public java.lang.String getInterfaceDescriptor() {
+                return DESCRIPTOR;
+            }
+
+            /**
+             * This method is called when the shutdown of MountService
+             * completed.
+             * 
+             * @param statusCode indicates success or failure of the shutdown.
+             */
+            public void onShutDownComplete(int statusCode) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(statusCode);
+                    mRemote.transact(Stub.TRANSACTION_onShutDownComplete, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+        }
+
+        static final int TRANSACTION_onShutDownComplete = (IBinder.FIRST_CALL_TRANSACTION + 0);
+    }
+
+    /**
+     * This method is called when the shutdown of MountService completed.
+     * 
+     * @param statusCode indicates success or failure of the shutdown.
+     */
+    public void onShutDownComplete(int statusCode) throws RemoteException;
+}
diff --git a/core/java/android/os/storage/IObbActionListener.aidl b/core/java/android/os/storage/IObbActionListener.aidl
deleted file mode 100644
index 78d7a9e..0000000
--- a/core/java/android/os/storage/IObbActionListener.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os.storage;
-
-/**
- * Callback class for receiving events from MountService about
- * Opaque Binary Blobs (OBBs).
- *
- * @hide - Applications should use android.os.storage.StorageManager
- * to interact with OBBs.
- */
-interface IObbActionListener {
-    /**
-     * Return from an OBB action result.
-     *
-     * @param filename the path to the OBB the operation was performed on
-     * @param returnCode status of the operation
-     */
-    void onObbResult(String filename, String status);
-}
diff --git a/core/java/android/os/storage/IObbActionListener.java b/core/java/android/os/storage/IObbActionListener.java
new file mode 100644
index 0000000..2c098ac
--- /dev/null
+++ b/core/java/android/os/storage/IObbActionListener.java
@@ -0,0 +1,130 @@
+/*
+ * 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.os.storage;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+/**
+ * Callback class for receiving events from MountService about Opaque Binary
+ * Blobs (OBBs).
+ * 
+ * @hide - Applications should use StorageManager to interact with OBBs.
+ */
+public interface IObbActionListener extends IInterface {
+    /** Local-side IPC implementation stub class. */
+    public static abstract class Stub extends Binder implements IObbActionListener {
+        private static final String DESCRIPTOR = "IObbActionListener";
+
+        /** Construct the stub at attach it to the interface. */
+        public Stub() {
+            this.attachInterface(this, DESCRIPTOR);
+        }
+
+        /**
+         * Cast an IBinder object into an IObbActionListener interface,
+         * generating a proxy if needed.
+         */
+        public static IObbActionListener asInterface(IBinder obj) {
+            if ((obj == null)) {
+                return null;
+            }
+            IInterface iin = (IInterface) obj.queryLocalInterface(DESCRIPTOR);
+            if (((iin != null) && (iin instanceof IObbActionListener))) {
+                return ((IObbActionListener) iin);
+            }
+            return new IObbActionListener.Stub.Proxy(obj);
+        }
+
+        public IBinder asBinder() {
+            return this;
+        }
+
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            switch (code) {
+                case INTERFACE_TRANSACTION: {
+                    reply.writeString(DESCRIPTOR);
+                    return true;
+                }
+                case TRANSACTION_onObbResult: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String filename;
+                    filename = data.readString();
+                    String status;
+                    status = data.readString();
+                    this.onObbResult(filename, status);
+                    reply.writeNoException();
+                    return true;
+                }
+            }
+            return super.onTransact(code, data, reply, flags);
+        }
+
+        private static class Proxy implements IObbActionListener {
+            private IBinder mRemote;
+
+            Proxy(IBinder remote) {
+                mRemote = remote;
+            }
+
+            public IBinder asBinder() {
+                return mRemote;
+            }
+
+            public String getInterfaceDescriptor() {
+                return DESCRIPTOR;
+            }
+
+            /**
+             * Return from an OBB action result.
+             * 
+             * @param filename the path to the OBB the operation was performed
+             *            on
+             * @param returnCode status of the operation
+             */
+            public void onObbResult(String filename, String status) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(filename);
+                    _data.writeString(status);
+                    mRemote.transact(Stub.TRANSACTION_onObbResult, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+        }
+
+        static final int TRANSACTION_onObbResult = (IBinder.FIRST_CALL_TRANSACTION + 0);
+    }
+
+    /**
+     * Return from an OBB action result.
+     * 
+     * @param filename the path to the OBB the operation was performed on
+     * @param returnCode status of the operation
+     */
+    public void onObbResult(String filename, String status) throws RemoteException;
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 7c9effa..4a0296b 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -295,6 +295,8 @@
      * file matches a package ID that is owned by the calling program's UID.
      * That is, shared UID applications can obtain access to any other
      * application's OBB that shares its UID.
+     * <p>
+     * STOPSHIP document more; discuss lack of guarantees of security
      * 
      * @param filename the path to the OBB file
      * @param key decryption key
@@ -319,6 +321,8 @@
      * file matches a package ID that is owned by the calling program's UID.
      * That is, shared UID applications can obtain access to any other
      * application's OBB that shares its UID.
+     * <p>
+     * STOPSHIP document more; discuss lack of guarantees of security
      * 
      * @param filename path to the OBB file
      * @param force whether to kill any programs using this in order to unmount
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 70a2b80..4889c19 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -42,6 +42,7 @@
 import android.view.View.OnClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.TextView;
@@ -158,6 +159,8 @@
 
     private HeaderAdapter mAdapter;
 
+    private FrameLayout mListFooter;
+
     private View mPrefsContainer;
 
     private boolean mSinglePane;
@@ -189,6 +192,10 @@
                 case MSG_BUILD_HEADERS:
                     onBuildHeaders(mHeaders);
                     mAdapter.notifyDataSetChanged();
+                    Header header = onGetNewHeader();
+                    if (header != null && header.fragment != null) {
+                        switchToHeader(header.fragment, header.fragmentArguments);
+                    }
                     break;
             }
         }
@@ -287,6 +294,11 @@
          * instantiated.
          */
         public Bundle fragmentArguments;
+
+        /**
+         * Intent to launch when the preference is selected.
+         */
+        public Intent intent;
     }
 
     @Override
@@ -295,6 +307,7 @@
 
         setContentView(com.android.internal.R.layout.preference_list_content);
 
+        mListFooter = (FrameLayout)findViewById(com.android.internal.R.id.list_footer);
         mPrefsContainer = findViewById(com.android.internal.R.id.prefs);
         boolean hidingHeaders = onIsHidingHeaders();
         mSinglePane = hidingHeaders || !onIsMultiPane();
@@ -446,6 +459,16 @@
     }
 
     /**
+     * Called after the header list has been updated ({@link #onBuildHeaders}
+     * has been called and returned due to {@link #invalidateHeaders()}) to
+     * specify the header that should now be selected.  The default implementation
+     * returns null to keep whatever header is currently selected.
+     */
+    public Header onGetNewHeader() {
+        return null;
+    }
+
+    /**
      * Called when the activity needs its list of headers build.  By
      * implementing this and adding at least one item to the list, you
      * will cause the activity to run in its modern fragment mode.  Note
@@ -498,7 +521,7 @@
 
             Bundle curBundle = null;
 
-            int outerDepth = parser.getDepth();
+            final int outerDepth = parser.getDepth();
             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -524,7 +547,27 @@
                     if (curBundle == null) {
                         curBundle = new Bundle();
                     }
-                    getResources().parseBundleExtras(parser, curBundle);
+
+                    final int innerDepth = parser.getDepth();
+                    while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                           && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+                        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                            continue;
+                        }
+
+                        String innerNodeName = parser.getName();
+                        if (innerNodeName.equals("extra")) {
+                            getResources().parseBundleExtra("extra", attrs, curBundle);
+                            XmlUtils.skipCurrentTag(parser);
+
+                        } else if (innerNodeName.equals("intent")) {
+                            header.intent = Intent.parseIntent(getResources(), parser, attrs);
+
+                        } else {
+                            XmlUtils.skipCurrentTag(parser);
+                        }
+                    }
+
                     if (curBundle.size() > 0) {
                         header.fragmentArguments = curBundle;
                         curBundle = null;
@@ -546,6 +589,16 @@
 
     }
 
+    /**
+     * Set a footer that should be shown at the bottom of the header list.
+     */
+    public void setListFooter(View view) {
+        mListFooter.removeAllViews();
+        mListFooter.addView(view, new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.WRAP_CONTENT));
+    }
+
     @Override
     protected void onStop() {
         super.onStop();
@@ -633,10 +686,14 @@
      * @param position The header's position in the list.
      */
     public void onHeaderClick(Header header, int position) {
-        if (mSinglePane) {
-            startWithFragment(header.fragment, header.fragmentArguments);
-        } else {
-            switchToHeader(header.fragment, header.fragmentArguments);
+        if (header.fragment != null) {
+            if (mSinglePane) {
+                startWithFragment(header.fragment, header.fragmentArguments);
+            } else {
+                switchToHeader(header.fragment, header.fragmentArguments);
+            }
+        } else if (header.intent != null) {
+            startActivity(header.intent);
         }
     }
 
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 276aa84..b87e0ec 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -755,7 +755,6 @@
      * 'family name', 'given name' 'middle name'.  The CJK tradition is
      * 'family name' 'middle name' 'given name', with Japanese favoring a space between
      * the names and Chinese omitting the space.
-     * @hide
      */
     public interface FullNameStyle {
         public static final int UNDEFINED = 0;
@@ -774,7 +773,6 @@
 
     /**
      * Constants for various styles of capturing the pronunciation of a person's name.
-     * @hide
      */
     public interface PhoneticNameStyle {
         public static final int UNDEFINED = 0;
@@ -800,8 +798,6 @@
     /**
      * Types of data used to produce the display name for a contact. Listed in the order
      * of increasing priority.
-     *
-     * @hide
      */
     public interface DisplayNameSources {
         public static final int UNDEFINED = 0;
@@ -817,15 +813,12 @@
      *
      * @see Contacts
      * @see RawContacts
-     * @hide
      */
     protected interface ContactNameColumns {
 
         /**
          * The kind of data that is used as the display name for the contact, such as
-         * structured name or email address.  See DisplayNameSources.
-         *
-         * TODO: convert DisplayNameSources to a link after it is un-hidden
+         * structured name or email address.  See {@link DisplayNameSources}.
          */
         public static final String DISPLAY_NAME_SOURCE = "display_name_source";
 
@@ -873,9 +866,7 @@
 
         /**
          * The phonetic alphabet used to represent the {@link #PHONETIC_NAME}.  See
-         * PhoneticNameStyle.
-         *
-         * TODO: convert PhoneticNameStyle to a link after it is un-hidden
+         * {@link PhoneticNameStyle}.
          */
         public static final String PHONETIC_NAME_STYLE = "phonetic_name_style";
 
@@ -885,13 +876,10 @@
          * {@link #PHONETIC_NAME_STYLE}.
          * </p>
          * <p>
-         * The value may be set manually by the user.
-         * This capability is is of interest only in countries
-         * with commonly used phonetic
-         * alphabets, such as Japan and Korea.  See PhoneticNameStyle.
+         * The value may be set manually by the user. This capability is of
+         * interest only in countries with commonly used phonetic alphabets,
+         * such as Japan and Korea. See {@link PhoneticNameStyle}.
          * </p>
-         *
-         * TODO: convert PhoneticNameStyle to a link after it is un-hidden
          */
         public static final String PHONETIC_NAME = "phonetic_name";
 
@@ -1374,8 +1362,6 @@
          * against the cost of transferring large amounts of denormalized data
          * from the Provider.
          * </p>
-         *
-         * @hide
          */
         public static final class Entity implements BaseColumns, ContactsColumns,
                 ContactNameColumns, RawContactsColumns, BaseSyncColumns, SyncColumns, DataColumns,
@@ -2763,6 +2749,7 @@
      * <li>{@link CommonDataKinds.Website Website.CONTENT_ITEM_TYPE}</li>
      * <li>{@link CommonDataKinds.Event Event.CONTENT_ITEM_TYPE}</li>
      * <li>{@link CommonDataKinds.Relation Relation.CONTENT_ITEM_TYPE}</li>
+     * <li>{@link CommonDataKinds.SipAddress SipAddress.CONTENT_ITEM_TYPE}</li>
      * </ul>
      * </p>
      * </td>
@@ -5318,6 +5305,52 @@
              */
             public static final String URL = DATA;
         }
+
+        /**
+         * <p>
+         * A data kind representing a SIP address for the contact.
+         * </p>
+         * <p>
+         * You can use all columns defined for {@link ContactsContract.Data} as
+         * well as the following aliases.
+         * </p>
+         * <h2>Column aliases</h2>
+         * <table class="jd-sumtable">
+         * <tr>
+         * <th>Type</th>
+         * <th>Alias</th><th colspan='2'>Data column</th>
+         * </tr>
+         * <tr>
+         * <td>String</td>
+         * <td>{@link #SIP_ADDRESS}</td>
+         * <td>{@link #DATA1}</td>
+         * <td></td>
+         * </tr>
+         * </table>
+         */
+        public static final class SipAddress implements DataColumnsWithJoins {
+            // TODO: Ultimately this class will probably implement
+            // CommonColumns too (in addition to DataColumnsWithJoins)
+            // since it may make sense to have multiple SIP addresses with
+            // different types+labels, just like with phone numbers.
+            //
+            // But that can be extended in the future without breaking any
+            // public API, so let's keep this class ultra-simple for now.
+
+            /**
+             * This utility class cannot be instantiated
+             */
+            private SipAddress() {}
+
+            /** MIME type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
+
+            /**
+             * The SIP address.
+             * <P>Type: TEXT</P>
+             */
+            public static final String SIP_ADDRESS = DATA1;
+        }
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 227d94d..60ccdaf 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -322,6 +322,8 @@
         private static final int MICRO_KIND = 3;
         private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
         static final int DEFAULT_GROUP_ID = 0;
+        private static final Object sThumbBufLock = new Object();
+        private static byte[] sThumbBuf;
 
         private static Bitmap getMiniThumbFromFile(Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
             Bitmap bitmap = null;
@@ -397,11 +399,15 @@
                 long magic = thumbFile.getMagic(origId);
                 if (magic != 0) {
                     if (kind == MICRO_KIND) {
-                        byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
-                        if (thumbFile.getMiniThumbFromFile(origId, data) != null) {
-                            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
-                            if (bitmap == null) {
-                                Log.w(TAG, "couldn't decode byte array.");
+                        synchronized (sThumbBufLock) {
+                            if (sThumbBuf == null) {
+                                sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
+                            }
+                            if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
+                                bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
+                                if (bitmap == null) {
+                                    Log.w(TAG, "couldn't decode byte array.");
+                                }
                             }
                         }
                         return bitmap;
@@ -427,11 +433,15 @@
 
                 // Assuming thumbnail has been generated, at least original image exists.
                 if (kind == MICRO_KIND) {
-                    byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
-                    if (thumbFile.getMiniThumbFromFile(origId, data) != null) {
-                        bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
-                        if (bitmap == null) {
-                            Log.w(TAG, "couldn't decode byte array.");
+                    synchronized (sThumbBufLock) {
+                        if (sThumbBuf == null) {
+                            sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
+                        }
+                        if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
+                            bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
+                            if (bitmap == null) {
+                                Log.w(TAG, "couldn't decode byte array.");
+                            }
                         }
                     }
                 } else if (kind == MINI_KIND) {
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Mtp.java
index bc764ac..26aa7d8 100644
--- a/core/java/android/provider/Mtp.java
+++ b/core/java/android/provider/Mtp.java
@@ -311,9 +311,12 @@
         // Object properties we support
         public static final int PROPERTY_STORAGE_ID = 0xDC01;
         public static final int PROPERTY_OBJECT_FORMAT = 0xDC02;
+        public static final int PROPERTY_PROTECTION_STATUS = 0xDC03;
         public static final int PROPERTY_OBJECT_SIZE = 0xDC04;
         public static final int PROPERTY_OBJECT_FILE_NAME = 0xDC07;
+        public static final int PROPERTY_DATE_MODIFIED = 0xDC09;
         public static final int PROPERTY_PARENT_OBJECT = 0xDC0B;
+        public static final int PROPERTY_PERSISTENT_UID = 0xDC41;
 
         /**
          * Object is not protected. It may be modified and deleted, and its properties
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 9948060..3861ef5 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -644,7 +644,7 @@
              } else {
                  Log.i(TAG, "Rejecting incoming HID connection from " + address);
              }
-        } else if (BluetoothUuid.isNAP(uuid)){
+        } else if (BluetoothUuid.isBnep(uuid) || BluetoothUuid.isNap(uuid)){
             authorized = true;
         } else {
             Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index ab78aeb..1808656 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1256,12 +1256,32 @@
         return mTetheringOn;
     }
 
-    public synchronized void setBluetoothTethering(boolean value, String uuid, String bridge) {
+    private BroadcastReceiver mTetheringReceiver = null;
+
+    public synchronized void setBluetoothTethering(boolean value, 
+            final String uuid, final String bridge) {
         mTetheringOn = value;
         if (!value) {
             disconnectPan();
         }
-        setBluetoothTetheringNative(value, uuid, bridge);
+
+        if (getBluetoothState() != BluetoothAdapter.STATE_ON && mTetheringOn) {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+            mTetheringReceiver = new BroadcastReceiver() {
+                @Override
+                public synchronized void onReceive(Context context, Intent intent) {
+                    if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
+                            == BluetoothAdapter.STATE_ON) {
+                        setBluetoothTethering(true, uuid, bridge);
+                        mContext.unregisterReceiver(mTetheringReceiver);
+                    }
+                }
+            };
+            mContext.registerReceiver(mTetheringReceiver, filter);
+        } else {
+            setBluetoothTetheringNative(value, uuid, bridge);
+        }
     }
 
     public synchronized int getPanDeviceState(BluetoothDevice device) {
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 8fa0d59..cd73ba8 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -50,7 +50,7 @@
     private static final String TAG = "SpeechRecognizer";
 
     /**
-     * Used to retrieve an {@code ArrayList&lt;String&gt;} from the {@link Bundle} passed to the
+     * Used to retrieve an {@code ArrayList<String>} from the {@link Bundle} passed to the
      * {@link RecognitionListener#onResults(Bundle)} and
      * {@link RecognitionListener#onPartialResults(Bundle)} methods. These strings are the possible
      * recognition results, where the first element is the most likely candidate.
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 0e3522e..2f7482c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -75,7 +75,7 @@
             }
         }
         tl = new TextLine();
-        Log.e("TLINE", "new: " + tl);
+        Log.v("TLINE", "new: " + tl);
         return tl;
     }
 
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 79a0c37..3fe14f9 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -287,8 +287,7 @@
                     // Offset the current touch position (from controller to cursor)
                     final float x = event.getX() + mCursorController.getOffsetX();
                     final float y = event.getY() + mCursorController.getOffsetY();
-                    int offset = widget.getOffset((int) x, (int) y);
-                    mCursorController.updatePosition(offset);
+                    mCursorController.updatePosition((int) x, (int) y);
                     return true;
 
                 case MotionEvent.ACTION_UP:
@@ -320,25 +319,9 @@
 
     public void onTakeFocus(TextView view, Spannable text, int dir) {
         if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) {
-            Layout layout = view.getLayout();
-
-            if (layout == null) {
-                /*
-                 * This shouldn't be null, but do something sensible if it is.
-                 */
+            if (view.getLayout() == null) {
+                // This shouldn't be null, but do something sensible if it is.
                 Selection.setSelection(text, text.length());
-            } else {
-                /*
-                 * Put the cursor at the end of the first line, which is
-                 * either the last offset if there is only one line, or the
-                 * offset before the first character of the second line
-                 * if there is more than one line.
-                 */
-                if (layout.getLineCount() == 1) {
-                    Selection.setSelection(text, text.length());
-                } else {
-                    Selection.setSelection(text, layout.getLineStart(1) - 1);
-                }
             }
         } else {
             Selection.setSelection(text, text.length());
diff --git a/core/java/android/util/TimingLogger.java b/core/java/android/util/TimingLogger.java
index 0f39c97..be442da 100644
--- a/core/java/android/util/TimingLogger.java
+++ b/core/java/android/util/TimingLogger.java
@@ -24,22 +24,26 @@
  * A utility class to help log timings splits throughout a method call.
  * Typical usage is:
  *
- * TimingLogger timings = new TimingLogger(TAG, "methodA");
- * ... do some work A ...
- * timings.addSplit("work A");
- * ... do some work B ...
- * timings.addSplit("work B");
- * ... do some work C ...
- * timings.addSplit("work C");
- * timings.dumpToLog();
+ * <pre>
+ *     TimingLogger timings = new TimingLogger(TAG, "methodA");
+ *     // ... do some work A ...
+ *     timings.addSplit("work A");
+ *     // ... do some work B ...
+ *     timings.addSplit("work B");
+ *     // ... do some work C ...
+ *     timings.addSplit("work C");
+ *     timings.dumpToLog();
+ * </pre>
  *
- * The dumpToLog call would add the following to the log:
+ * <p>The dumpToLog call would add the following to the log:</p>
  *
- * D/TAG     ( 3459): methodA: begin
- * D/TAG     ( 3459): methodA:      9 ms, work A
- * D/TAG     ( 3459): methodA:      1 ms, work B
- * D/TAG     ( 3459): methodA:      6 ms, work C
- * D/TAG     ( 3459): methodA: end, 16 ms
+ * <pre>
+ *     D/TAG     ( 3459): methodA: begin
+ *     D/TAG     ( 3459): methodA:      9 ms, work A
+ *     D/TAG     ( 3459): methodA:      1 ms, work B
+ *     D/TAG     ( 3459): methodA:      6 ms, work C
+ *     D/TAG     ( 3459): methodA: end, 16 ms
+ * </pre>
  */
 public class TimingLogger {
 
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 4c72e95..b058685 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -44,7 +44,7 @@
     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
     private final GL mGl;
     private final boolean mOpaque;
-    private final int mRenderer;
+    private int mRenderer;
     
     private int mWidth;
     private int mHeight;
@@ -76,16 +76,25 @@
         mOpaque = !translucent;
 
         mRenderer = nCreateRenderer();
+        if (mRenderer == 0) {
+            throw new IllegalStateException("Could not create GLES20Canvas renderer");
+        }
     }
-
+    
     private native int nCreateRenderer();
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            super.finalize();
-        } finally {
+    /**
+     * This method <strong>must</strong> be called before releasing a
+     * reference to a GLES20Canvas. This method is responsible for freeing
+     * native resources associated with the hardware. Not invoking this
+     * method properly can result in memory leaks.
+     * 
+     * @hide
+     */
+    public synchronized void destroy() {
+        if (mRenderer != 0) {
             nDestroyRenderer(mRenderer);
+            mRenderer = 0;
         }
     }
 
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index ca60a89..42e3641 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -35,9 +35,10 @@
  * @hide
  */
 public abstract class HardwareRenderer {
+    private static final String LOG_TAG = "HardwareRenderer";
+
     private boolean mEnabled;
     private boolean mRequested = true;
-    private static final String LOG_TAG = "HardwareRenderer";
 
     /**
      * Indicates whether hardware acceleration is available under any form for
@@ -52,8 +53,10 @@
 
     /**
      * Destroys the hardware rendering context.
+     * 
+     * @param full If true, destroys all associated resources.
      */
-    abstract void destroy();
+    abstract void destroy(boolean full);
 
     /**
      * Initializes the hardware renderer for the specified surface.
@@ -70,9 +73,8 @@
      * 
      * @param width Width of the drawing surface.
      * @param height Height of the drawing surface.
-     * @param attachInfo The AttachInfo used to render the ViewRoot. 
      */
-    abstract void setup(int width, int height, View.AttachInfo attachInfo);
+    abstract void setup(int width, int height);
 
     /**
      * Draws the specified view.
@@ -96,12 +98,11 @@
      */
     void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
             SurfaceHolder holder) {
-
         if (isRequested()) {
             // We lost the gl context, so recreate it.
             if (!isEnabled()) {
                 if (initialize(holder)) {
-                    setup(width, height, attachInfo);
+                    setup(width, height);
                 }
             }
         }        
@@ -165,35 +166,40 @@
     static abstract class GlRenderer extends HardwareRenderer {
         private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
 
-        EGL10 mEgl;
-        EGLDisplay mEglDisplay;
-        EGLContext mEglContext;
-        EGLSurface mEglSurface;
-        EGLConfig mEglConfig;
+        static EGLContext sEglContext;
+        static EGL10 sEgl;
+        static EGLDisplay sEglDisplay;
+        static EGLConfig sEglConfig;
 
+        private static Thread sEglThread;        
+
+        EGLSurface mEglSurface;
+        
         GL mGl;
-        Canvas mCanvas;
+        GLES20Canvas mCanvas;
 
         final int mGlVersion;
         final boolean mTranslucent;
 
+        private boolean mDestroyed;
+
         GlRenderer(int glVersion, boolean translucent) {
             mGlVersion = glVersion;
             mTranslucent = translucent;
         }
 
         /**
-         * Checks for OpenGL errors. If an error has occured, {@link #destroy()}
+         * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
          * is invoked and the requested flag is turned off. The error code is
          * also logged as a warning.
          */
         void checkErrors() {
             if (isEnabled()) {
-                int error = mEgl.eglGetError();
+                int error = sEgl.eglGetError();
                 if (error != EGL10.EGL_SUCCESS) {
                     // something bad has happened revert to
                     // normal rendering.
-                    destroy();
+                    destroy(true);
                     if (error != EGL11.EGL_CONTEXT_LOST) {
                         // we'll try again if it was context lost
                         setRequested(false);
@@ -208,14 +214,17 @@
             if (isRequested() && !isEnabled()) {
                 initializeEgl();
                 mGl = createEglSurface(holder);
+                mDestroyed = false;
 
                 if (mGl != null) {
-                    int err = mEgl.eglGetError();
+                    int err = sEgl.eglGetError();
                     if (err != EGL10.EGL_SUCCESS) {
-                        destroy();
+                        destroy(true);
                         setRequested(false);
                     } else {
-                        mCanvas = createCanvas();
+                        if (mCanvas == null) {
+                            mCanvas = createCanvas();
+                        }
                         if (mCanvas != null) {
                             setEnabled(true);
                         } else {
@@ -229,64 +238,70 @@
             return false;
         }
 
-        abstract Canvas createCanvas();
+        abstract GLES20Canvas createCanvas();
 
         void initializeEgl() {
-            mEgl = (EGL10) EGLContext.getEGL();
+            if (sEglContext != null) return;
+
+            sEglThread = Thread.currentThread();
+            sEgl = (EGL10) EGLContext.getEGL();
             
             // Get to the default display.
-            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+            sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
             
-            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+            if (sEglDisplay == EGL10.EGL_NO_DISPLAY) {
                 throw new RuntimeException("eglGetDisplay failed");
             }
             
             // We can now initialize EGL for that display
             int[] version = new int[2];
-            if (!mEgl.eglInitialize(mEglDisplay, version)) {
+            if (!sEgl.eglInitialize(sEglDisplay, version)) {
                 throw new RuntimeException("eglInitialize failed");
             }
-            mEglConfig = getConfigChooser(mGlVersion).chooseConfig(mEgl, mEglDisplay);
+            sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
             
             /*
             * Create an EGL context. We want to do this as rarely as we can, because an
             * EGL context is a somewhat heavy object.
             */
-            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
+            sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
         }
 
         GL createEglSurface(SurfaceHolder holder) {
             // Check preconditions.
-            if (mEgl == null) {
+            if (sEgl == null) {
                 throw new RuntimeException("egl not initialized");
             }
-            if (mEglDisplay == null) {
+            if (sEglDisplay == null) {
                 throw new RuntimeException("eglDisplay not initialized");
             }
-            if (mEglConfig == null) {
+            if (sEglConfig == null) {
                 throw new RuntimeException("mEglConfig not initialized");
             }
+            if (Thread.currentThread() != sEglThread) {
+                throw new IllegalStateException("HardwareRenderer cannot be used " 
+                        + "from multiple threads");
+            }
 
             /*
              *  The window size has changed, so we need to create a new
              *  surface.
              */
             if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
-
                 /*
                  * Unbind and destroy the old EGL surface, if
                  * there is one.
                  */
-                mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+                sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
                         EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
-                mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+                sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
             }
 
             // Create an EGL surface we can render into.
-            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, null);
+            mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null);
 
             if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
-                int error = mEgl.eglGetError();
+                int error = sEgl.eglGetError();
                 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
                     Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
                     return null;
@@ -298,11 +313,11 @@
              * Before we can issue GL commands, we need to make sure
              * the context is current and bound to a surface.
              */
-            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+            if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
                 throw new RuntimeException("eglMakeCurrent failed");
             }
 
-            return mEglContext.getGL();
+            return sEglContext.getGL();
         }
 
         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
@@ -315,7 +330,6 @@
         @Override
         void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
                 SurfaceHolder holder) {
-
             if (isRequested()) {
                 checkErrors();
                 super.initializeIfNeeded(width, height, attachInfo, holder);
@@ -323,29 +337,29 @@
         }
         
         @Override
-        void destroy() {
-            if (!isEnabled()) return;
+        void destroy(boolean full) {
+            if (full && mCanvas != null) {
+                mCanvas.destroy();
+                mCanvas = null;
+            }
+            
+            if (!isEnabled() || mDestroyed) return;
 
-            mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+            mDestroyed = true;
+
+            sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
                     EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
-            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
-            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
-            mEgl.eglTerminate(mEglDisplay);
+            sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
 
-            mEglContext = null;
             mEglSurface = null;
-            mEglDisplay = null;
-            mEgl = null;
             mGl = null;
-            mCanvas = null;
 
             setEnabled(false);
-        }        
-        
+        }
+
         @Override
-        void setup(int width, int height, View.AttachInfo attachInfo) {
-            final float scale = attachInfo.mApplicationScale;
-            mCanvas.setViewport((int) (width * scale + 0.5f), (int) (height * scale + 0.5f));
+        void setup(int width, int height) {
+            mCanvas.setViewport(width, height);
         }
         
         boolean canDraw() {
@@ -363,7 +377,8 @@
          * @param glVersion
          */
         EglConfigChooser getConfigChooser(int glVersion) {
-            return new ComponentSizeChooser(glVersion, 8, 8, 8, mTranslucent ? 8 : 0, 0, 0);
+            // TODO: Check for mTranslucent here, which means at least 2 EGL contexts
+            return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0);
         }
 
         @Override
@@ -373,14 +388,7 @@
                 attachInfo.mIgnoreDirtyState = true;
                 view.mPrivateFlags |= View.DRAWN;
 
-                // TODO: Don't check the current context when we have one per UI thread
-                // TODO: Use a threadlocal flag to know whether the surface has changed
-                if (mEgl.eglGetCurrentContext() != mEglContext ||
-                        mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
-                    if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
-                        throw new RuntimeException("eglMakeCurrent failed");
-                    }
-                }
+                checkCurrent();
 
                 onPreDraw();
 
@@ -396,11 +404,22 @@
 
                 attachInfo.mIgnoreDirtyState = false;
 
-                mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+                sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
                 checkErrors();
             }
         }
 
+        private void checkCurrent() {
+            // TODO: Don't check the current context when we have one per UI thread
+            // TODO: Use a threadlocal flag to know whether the surface has changed
+            if (sEgl.eglGetCurrentContext() != sEglContext ||
+                    sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
+                if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
+                    throw new RuntimeException("eglMakeCurrent failed");
+                }
+            }
+        }
+
         static abstract class EglConfigChooser {
             final int[] mConfigSpec;
             private final int mGlVersion;
@@ -496,7 +515,7 @@
                         int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
                         int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
                         int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
-                        if (r == mRedSize && g == mGreenSize && b == mBlueSize && a >= mAlphaSize) {
+                        if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize) {
                             return config;
                         }
                     }
@@ -514,7 +533,7 @@
             }
         }
     }
-    
+
     /**
      * Hardware renderer using OpenGL ES 2.0.
      */
@@ -526,8 +545,9 @@
         }
 
         @Override
-        Canvas createCanvas() {
-            return mGlCanvas = new GLES20Canvas(mGl, mTranslucent);
+        GLES20Canvas createCanvas() {
+            // TODO: Pass mTranslucent instead of true
+            return mGlCanvas = new GLES20Canvas(mGl, true);
         }
 
         @Override
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 479e757..458274f 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -349,6 +349,7 @@
     public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
         synchronized (mConstructorArgs) {
             final AttributeSet attrs = Xml.asAttributeSet(parser);
+            Context lastContext = (Context)mConstructorArgs[0];
             mConstructorArgs[0] = mContext;
             View result = root;
 
@@ -433,6 +434,10 @@
                         + ": " + e.getMessage());
                 ex.initCause(e);
                 throw ex;
+            } finally {
+                // Don't retain static reference on context.
+                mConstructorArgs[0] = lastContext;
+                mConstructorArgs[1] = null;
             }
 
             return result;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 1328525..74318ba 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1319,21 +1319,24 @@
      * @param y New absolute Y location.
      */
     public final void setLocation(float x, float y) {
-        mXOffset = x - mDataSamples[mLastDataSampleIndex + SAMPLE_X];
-        mYOffset = y - mDataSamples[mLastDataSampleIndex + SAMPLE_Y];
+        final float[] dataSamples = mDataSamples;
+        final int lastDataSampleIndex = mLastDataSampleIndex;
+        mXOffset = x - dataSamples[lastDataSampleIndex + SAMPLE_X];
+        mYOffset = y - dataSamples[lastDataSampleIndex + SAMPLE_Y];
     }
     
     private final void getPointerCoordsAtSampleIndex(int sampleIndex,
             PointerCoords outPointerCoords) {
-        outPointerCoords.x = mDataSamples[sampleIndex + SAMPLE_X] + mXOffset;
-        outPointerCoords.y = mDataSamples[sampleIndex + SAMPLE_Y] + mYOffset;
-        outPointerCoords.pressure = mDataSamples[sampleIndex + SAMPLE_PRESSURE];
-        outPointerCoords.size = mDataSamples[sampleIndex + SAMPLE_SIZE];
-        outPointerCoords.touchMajor = mDataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR];
-        outPointerCoords.touchMinor = mDataSamples[sampleIndex + SAMPLE_TOUCH_MINOR];
-        outPointerCoords.toolMajor = mDataSamples[sampleIndex + SAMPLE_TOOL_MAJOR];
-        outPointerCoords.toolMinor = mDataSamples[sampleIndex + SAMPLE_TOOL_MINOR];
-        outPointerCoords.orientation = mDataSamples[sampleIndex + SAMPLE_ORIENTATION];
+        final float[] dataSamples = mDataSamples;
+        outPointerCoords.x = dataSamples[sampleIndex + SAMPLE_X] + mXOffset;
+        outPointerCoords.y = dataSamples[sampleIndex + SAMPLE_Y] + mYOffset;
+        outPointerCoords.pressure = dataSamples[sampleIndex + SAMPLE_PRESSURE];
+        outPointerCoords.size = dataSamples[sampleIndex + SAMPLE_SIZE];
+        outPointerCoords.touchMajor = dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR];
+        outPointerCoords.touchMinor = dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR];
+        outPointerCoords.toolMajor = dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR];
+        outPointerCoords.toolMinor = dataSamples[sampleIndex + SAMPLE_TOOL_MINOR];
+        outPointerCoords.orientation = dataSamples[sampleIndex + SAMPLE_ORIENTATION];
     }
     
     private final void setPointerCoordsAtSampleIndex(int sampleIndex,
@@ -1347,28 +1350,30 @@
     
     private final void setPointerCoordsAtSampleIndex(int sampleIndex,
             PointerCoords pointerCoords) {
-        mDataSamples[sampleIndex + SAMPLE_X] = pointerCoords.x - mXOffset;
-        mDataSamples[sampleIndex + SAMPLE_Y] = pointerCoords.y - mYOffset;
-        mDataSamples[sampleIndex + SAMPLE_PRESSURE] = pointerCoords.pressure;
-        mDataSamples[sampleIndex + SAMPLE_SIZE] = pointerCoords.size;
-        mDataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pointerCoords.touchMajor;
-        mDataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pointerCoords.touchMinor;
-        mDataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = pointerCoords.toolMajor;
-        mDataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = pointerCoords.toolMinor;
-        mDataSamples[sampleIndex + SAMPLE_ORIENTATION] = pointerCoords.orientation;
+        final float[] dataSamples = mDataSamples;
+        dataSamples[sampleIndex + SAMPLE_X] = pointerCoords.x - mXOffset;
+        dataSamples[sampleIndex + SAMPLE_Y] = pointerCoords.y - mYOffset;
+        dataSamples[sampleIndex + SAMPLE_PRESSURE] = pointerCoords.pressure;
+        dataSamples[sampleIndex + SAMPLE_SIZE] = pointerCoords.size;
+        dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pointerCoords.touchMajor;
+        dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pointerCoords.touchMinor;
+        dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = pointerCoords.toolMajor;
+        dataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = pointerCoords.toolMinor;
+        dataSamples[sampleIndex + SAMPLE_ORIENTATION] = pointerCoords.orientation;
     }
     
     private final void setPointerCoordsAtSampleIndex(int sampleIndex,
             float x, float y, float pressure, float size) {
-        mDataSamples[sampleIndex + SAMPLE_X] = x - mXOffset;
-        mDataSamples[sampleIndex + SAMPLE_Y] = y - mYOffset;
-        mDataSamples[sampleIndex + SAMPLE_PRESSURE] = pressure;
-        mDataSamples[sampleIndex + SAMPLE_SIZE] = size;
-        mDataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pressure;
-        mDataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pressure;
-        mDataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = size;
-        mDataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = size;
-        mDataSamples[sampleIndex + SAMPLE_ORIENTATION] = 0;
+        final float[] dataSamples = mDataSamples;
+        dataSamples[sampleIndex + SAMPLE_X] = x - mXOffset;
+        dataSamples[sampleIndex + SAMPLE_Y] = y - mYOffset;
+        dataSamples[sampleIndex + SAMPLE_PRESSURE] = pressure;
+        dataSamples[sampleIndex + SAMPLE_SIZE] = size;
+        dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pressure;
+        dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pressure;
+        dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = size;
+        dataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = size;
+        dataSamples[sampleIndex + SAMPLE_ORIENTATION] = 0;
     }
     
     private final void incrementNumSamplesAndReserveStorage(int dataSampleStride) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c13bb8c..616128d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -262,7 +262,8 @@
  * <ul>
  * <li>Define a Button in the layout file and assign it a unique ID.
  * <pre>
- * &lt;Button id="@+id/my_button"
+ * &lt;Button
+ *     android:id="@+id/my_button"
  *     android:layout_width="wrap_content"
  *     android:layout_height="wrap_content"
  *     android:text="@string/my_button_text"/&gt;
@@ -3438,7 +3439,7 @@
     }
 
     /**
-     * Sets the pressed that for this view.
+     * Sets the pressed state for this view.
      *
      * @see #isClickable()
      * @see #setClickable(boolean)
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 402443c..7b6991f 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1138,22 +1138,24 @@
 
         final View captureView = findView(root, parameter);
         Bitmap b = performViewCapture(captureView, false);
-       
-        if (b != null) {
-            BufferedOutputStream out = null;
-            try {
-                out = new BufferedOutputStream(clientStream, 32 * 1024);
-                b.compress(Bitmap.CompressFormat.PNG, 100, out);
-                out.flush();
-            } finally {
-                if (out != null) {
-                    out.close();
-                }
-                b.recycle();
-            }
-        } else {
+
+        if (b == null) {
             Log.w("View", "Failed to create capture bitmap!");
-            clientStream.close();
+            // Send an empty one so that it doesn't get stuck waiting for
+            // something.
+            b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        }
+
+        BufferedOutputStream out = null;
+        try {
+            out = new BufferedOutputStream(clientStream, 32 * 1024);
+            b.compress(Bitmap.CompressFormat.PNG, 100, out);
+            out.flush();
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+            b.recycle();
         }
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index e2e3333..9770313 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -856,6 +856,9 @@
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         if ((mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS) {
+            if (mSplitMotionTargets == null) {
+                mSplitMotionTargets = new SplitMotionTargets();
+            }
             return dispatchSplitTouchEvent(ev);
         }
 
@@ -967,7 +970,12 @@
             mMotionTarget = null;
         }
 
-        return target.dispatchTouchEvent(ev);
+        if (target.dispatchTouchEvent(ev)) {
+            return true;
+        } else {
+            ev.setLocation(xf, yf);
+        }
+        return false;
     }
 
     /**
@@ -1003,7 +1011,12 @@
             // There are many subtle interactions in touch event dispatch; change at your own risk.
             child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
             ev.offsetLocation(localX - x, localY - y);
-            return child.dispatchTouchEvent(ev);
+            if (child.dispatchTouchEvent(ev)) {
+                return true;
+            } else {
+                ev.offsetLocation(x - localX, y - localY);
+                return false;
+            }
         }
         return false;
     }
@@ -1036,19 +1049,21 @@
                 ev.setAction(action);
                 // We know we want to dispatch the event down, try to find a child
                 // who can handle it, start with the front-most child.
+                final long downTime = ev.getEventTime();
                 final View[] children = mChildren;
                 final int count = mChildrenCount;
                 for (int i = count - 1; i >= 0; i--) {
                     final View child = children[i];
                     if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
                             || child.getAnimation() != null) {
-                        final MotionEvent childEvent = targets.filterMotionEventForChild(ev, child);
+                        final MotionEvent childEvent =
+                                targets.filterMotionEventForChild(ev, child, downTime);
                         if (childEvent != null) {
                             try {
                                 final int childActionIndex = childEvent.findPointerIndex(actionId);
                                 if (dispatchTouchEventIfInView(child, childEvent,
                                         childActionIndex)) {
-                                    targets.add(actionId, child);
+                                    targets.add(actionId, child, downTime);
 
                                     return true;
                                 }
@@ -1060,10 +1075,11 @@
                 }
 
                 // Didn't find a new target. Do we have a "primary" target to send to?
-                final View primaryTarget = targets.getPrimaryTarget();
-                if (primaryTarget != null) {
-                    final MotionEvent childEvent =
-                            targets.filterMotionEventForChild(ev, primaryTarget);
+                final SplitMotionTargets.TargetInfo primaryTargetInfo = targets.getPrimaryTarget();
+                if (primaryTargetInfo != null) {
+                    final View primaryTarget = primaryTargetInfo.view;
+                    final MotionEvent childEvent = targets.filterMotionEventForChild(ev,
+                            primaryTarget, primaryTargetInfo.downTime);
                     if (childEvent != null) {
                         try {
                             // Calculate the offset point into the target's local coordinates
@@ -1081,7 +1097,7 @@
                             }
                             childEvent.setLocation(xc, yc);
                             if (primaryTarget.dispatchTouchEvent(childEvent)) {
-                                targets.add(actionId, primaryTarget);
+                                targets.add(actionId, primaryTarget, primaryTargetInfo.downTime);
                                 return true;
                             }
                         } finally {
@@ -1119,7 +1135,7 @@
             mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
 
             for (int uniqueIndex = 0; uniqueIndex < uniqueTargetCount; uniqueIndex++) {
-                final View target = targets.getUniqueTargetAt(uniqueIndex);
+                final View target = targets.getUniqueTargetAt(uniqueIndex).view;
 
                 // Calculate the offset point into the target's local coordinates
                 float xc = scrolledXFloat - (float) target.mLeft;
@@ -1150,9 +1166,11 @@
 
         boolean handled = false;
         for (int uniqueIndex = 0; uniqueIndex < uniqueTargetCount; uniqueIndex++) {
-            final View target = targets.getUniqueTargetAt(uniqueIndex);
+            final SplitMotionTargets.TargetInfo targetInfo = targets.getUniqueTargetAt(uniqueIndex);
+            final View target = targetInfo.view;
 
-            final MotionEvent targetEvent = targets.filterMotionEventForChild(ev, target);
+            final MotionEvent targetEvent =
+                    targets.filterMotionEventForChild(ev, target, targetInfo.downTime);
             if (targetEvent == null) {
                 continue;
             }
@@ -1187,7 +1205,13 @@
                     uniqueTargetCount--;
                 }
 
-                handled |= target.dispatchTouchEvent(targetEvent);
+                final boolean childHandled = target.dispatchTouchEvent(targetEvent);
+                handled |= childHandled;
+                if (!childHandled) {
+                    // Child doesn't want these events anymore, but we're still dispatching
+                    // other split events to children.
+                    targets.removeView(target);
+                }
             } finally {
                 targetEvent.recycle();
             }
@@ -1223,9 +1247,6 @@
         // but perhaps this should handle that case and send ACTION_CANCELs to any child views
         // with gestures in progress when this is changed.
         if (split) {
-            if ((mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == 0) {
-                mSplitMotionTargets = new SplitMotionTargets();
-            }
             mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
         } else {
             mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
@@ -4103,9 +4124,8 @@
 
     private static class SplitMotionTargets {
         private SparseArray<View> mTargets;
-        private View[] mUniqueTargets;
+        private TargetInfo[] mUniqueTargets;
         private int mUniqueTargetCount;
-        private long mDownTime;
         private MotionEvent.PointerCoords[] mPointerCoords;
         private int[] mPointerIds;
 
@@ -4114,7 +4134,7 @@
 
         public SplitMotionTargets() {
             mTargets = new SparseArray<View>();
-            mUniqueTargets = new View[INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
+            mUniqueTargets = new TargetInfo[INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
             mPointerIds = new int[INITIAL_BUCKET_SIZE];
             mPointerCoords = new MotionEvent.PointerCoords[INITIAL_BUCKET_SIZE];
             for (int i = 0; i < INITIAL_BUCKET_SIZE; i++) {
@@ -4124,31 +4144,32 @@
 
         public void clear() {
             mTargets.clear();
-            Arrays.fill(mUniqueTargets, null);
+            final int count = mUniqueTargetCount;
+            for (int i = 0; i < count; i++) {
+                mUniqueTargets[i].recycle();
+                mUniqueTargets[i] = null;
+            }
             mUniqueTargetCount = 0;
         }
 
-        public void add(int pointerId, View target) {
+        public void add(int pointerId, View target, long downTime) {
             mTargets.put(pointerId, target);
 
             final int uniqueCount = mUniqueTargetCount;
             boolean addUnique = true;
             for (int i = 0; i < uniqueCount; i++) {
-                if (mUniqueTargets[i] == target) {
+                if (mUniqueTargets[i].view == target) {
                     addUnique = false;
                 }
             }
             if (addUnique) {
-                if (mUniqueTargets == null) {
-                    mUniqueTargets = new View[INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
-                }
                 if (mUniqueTargets.length == uniqueCount) {
-                    View[] newTargets =
-                        new View[uniqueCount + INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
+                    TargetInfo[] newTargets =
+                        new TargetInfo[uniqueCount + INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
                     System.arraycopy(mUniqueTargets, 0, newTargets, 0, uniqueCount);
                     mUniqueTargets = newTargets;
                 }
-                mUniqueTargets[uniqueCount] = target;
+                mUniqueTargets[uniqueCount] = TargetInfo.obtain(target, downTime);
                 mUniqueTargetCount++;
             }
         }
@@ -4161,7 +4182,7 @@
             return mUniqueTargetCount;
         }
 
-        public View getUniqueTargetAt(int index) {
+        public TargetInfo getUniqueTargetAt(int index) {
             return mUniqueTargets[index];
         }
 
@@ -4185,18 +4206,29 @@
             return mTargets.valueAt(index);
         }
 
-        public View getPrimaryTarget() {
+        public TargetInfo getPrimaryTarget() {
             if (!isEmpty()) {
-                return mUniqueTargets[0];
+                // Find the longest-lived target
+                long firstTime = Long.MAX_VALUE;
+                int firstIndex = 0;
+                final int uniqueCount = mUniqueTargetCount;
+                for (int i = 0; i < uniqueCount; i++) {
+                    TargetInfo info = mUniqueTargets[i];
+                    if (info.downTime < firstTime) {
+                        firstTime = info.downTime;
+                        firstIndex = i;
+                    }
+                }
+                return mUniqueTargets[firstIndex];
             }
             return null;
         }
 
         public boolean hasTarget(View target) {
-            final View[] unique = mUniqueTargets;
+            final TargetInfo[] unique = mUniqueTargets;
             final int uniqueCount = mUniqueTargetCount;
             for (int i = 0; i < uniqueCount; i++) {
-                if (unique[i] == target) {
+                if (unique[i].view == target) {
                     return true;
                 }
             }
@@ -4237,10 +4269,11 @@
         }
 
         private void removeUnique(View removeView) {
-            View[] unique = mUniqueTargets;
+            TargetInfo[] unique = mUniqueTargets;
             int uniqueCount = mUniqueTargetCount;
             for (int i = 0; i < uniqueCount; i++) {
-                if (unique[i] == removeView) {
+                if (unique[i].view == removeView) {
+                    unique[i].recycle();
                     unique[i] = unique[--uniqueCount];
                     unique[uniqueCount] = null;
                     break;
@@ -4254,7 +4287,7 @@
          * Return a new (obtain()ed) MotionEvent containing only data for pointers that should
          * be dispatched to child. Don't forget to recycle it!
          */
-        public MotionEvent filterMotionEventForChild(MotionEvent ev, View child) {
+        public MotionEvent filterMotionEventForChild(MotionEvent ev, View child, long downTime) {
             int action = ev.getAction();
             final int maskedAction = action & MotionEvent.ACTION_MASK;
 
@@ -4279,7 +4312,6 @@
             if (maskedAction == MotionEvent.ACTION_DOWN) {
                 pointerCount++;
                 actionId = ev.getPointerId(0);
-                mDownTime = ev.getDownTime();
             } else if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
                 pointerCount++;
 
@@ -4352,11 +4384,49 @@
                         (newActionIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
             }
 
-            MotionEvent result = MotionEvent.obtain(mDownTime, ev.getEventTime(),
+            MotionEvent result = 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 {
+            public View view;
+            public long downTime;
+
+            private TargetInfo mNextRecycled;
+
+            private static TargetInfo sRecycleBin;
+            private static int sRecycledCount;
+
+            private static int MAX_RECYCLED = 15;
+
+            private TargetInfo() {
+            }
+
+            public static TargetInfo obtain(View v, long time) {
+                TargetInfo info;
+                if (sRecycleBin == null) {
+                    info = new TargetInfo();
+                } else {
+                    info = sRecycleBin;
+                    sRecycleBin = info.mNextRecycled;
+                    sRecycledCount--;
+                }
+                info.view = v;
+                info.downTime = time;
+                return info;
+            }
+
+            public void recycle() {
+                if (sRecycledCount >= MAX_RECYCLED) {
+                    return;
+                }
+                mNextRecycled = sRecycleBin;
+                sRecycleBin = this;
+                sRecycledCount++;
+            }
+        }
     }
 }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index d32ccb1..a91d2bd 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -251,7 +251,7 @@
         mTempRect = new Rect();
         mVisRect = new Rect();
         mWinFrame = new Rect();
-        mWindow = new W(this, context);
+        mWindow = new W(this);
         mInputMethodCallback = new InputMethodCallback(this);
         mViewVisibility = View.GONE;
         mTransparentRegion = new Region();
@@ -469,6 +469,9 @@
             if (attrs != null &&
                     (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
                 final boolean translucent = attrs.format != PixelFormat.OPAQUE;
+                if (mHwRenderer != null) {
+                    mHwRenderer.destroy(true);
+                }                
                 mHwRenderer = HardwareRenderer.createGlRenderer(2, translucent);
             }
         }
@@ -678,8 +681,8 @@
             host.dispatchWindowVisibilityChanged(viewVisibility);
             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
                 if (mHwRenderer != null) {
-                    mHwRenderer.destroy();
-                }
+                    mHwRenderer.destroy(false);
+                }                
             }
             if (viewVisibility == View.GONE) {
                 // After making a window gone, we will count it as being
@@ -963,7 +966,7 @@
             }
             
             if (hwIntialized) {
-                mHwRenderer.setup(mWidth, mHeight, mAttachInfo);
+                mHwRenderer.setup(mWidth, mHeight);
             }
 
             boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
@@ -1599,8 +1602,10 @@
         mAttachInfo.mSurface = null;
 
         if (mHwRenderer != null) {
-            mHwRenderer.destroy();
+            mHwRenderer.destroy(true);
+            mHwRenderer = null;
         }
+
         mSurface.release();
 
         if (mInputChannel != null) {
@@ -2734,10 +2739,6 @@
     public void childDrawableStateChanged(View child) {
     }
 
-    protected Rect getWindowFrame() {
-        return mWinFrame;
-    }
-
     void checkThread() {
         if (mThread != Thread.currentThread()) {
             throw new CalledFromWrongThreadException(
@@ -2816,16 +2817,15 @@
     static class W extends IWindow.Stub {
         private final WeakReference<ViewRoot> mViewRoot;
 
-        public W(ViewRoot viewRoot, Context context) {
+        W(ViewRoot viewRoot) {
             mViewRoot = new WeakReference<ViewRoot>(viewRoot);
         }
 
-        public void resized(int w, int h, Rect coveredInsets,
-                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+        public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets,
+                boolean reportDraw, Configuration newConfig) {
             final ViewRoot viewRoot = mViewRoot.get();
             if (viewRoot != null) {
-                viewRoot.dispatchResized(w, h, coveredInsets,
-                        visibleInsets, reportDraw, newConfig);
+                viewRoot.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig);
             }
         }
 
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 705af0f..6633367 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -771,6 +771,15 @@
         return (mFeatures&flag) != 0;
     }
 
+    /**
+     * @hide Used internally to help resolve conflicting features.
+     */
+    protected void removeFeature(int featureId) {
+        final int flag = 1<<featureId;
+        mFeatures &= ~flag;
+        mLocalFeatures &= ~(mContainer != null ? (flag&~mContainer.mFeatures) : flag);
+    }
+
     public final void makeActive() {
         if (mContainer != null) {
             if (mContainer.mActiveChild != null) {
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index fed55dc..55d11bb 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -103,11 +103,11 @@
         }
     }
 
-    public int getCurrentRotation() {
+    public int getCurrentRotation(int lastRotation) {
         if (mEnabled) {
-            return mSensorEventListener.getCurrentRotation();
+            return mSensorEventListener.getCurrentRotation(lastRotation);
         }
-        return -1;
+        return lastRotation;
     }
 
     /**
@@ -153,9 +153,15 @@
         private static final int ROTATION_270 = 2;
 
         // Mapping our internal aliases into actual Surface rotation values
-        private static final int[] SURFACE_ROTATIONS = new int[] {
+        private static final int[] INTERNAL_TO_SURFACE_ROTATION = new int[] {
             Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
 
+        // Mapping Surface rotation values to internal aliases.
+        // We have no constant for Surface.ROTATION_180.  That should never happen, but if it
+        // does, we'll arbitrarily choose a mapping.
+        private static final int[] SURFACE_TO_INTERNAL_ROTATION = new int[] {
+            ROTATION_0, ROTATION_90, ROTATION_90, ROTATION_270};
+
         // Threshold ranges of orientation angle to transition into other orientation states.
         // The first list is for transitions from ROTATION_0, the next for ROTATION_90, etc.
         // ROTATE_TO defines the orientation each threshold range transitions to, and must be kept
@@ -243,8 +249,12 @@
             return (float) SAMPLING_PERIOD_MS / (timeConstantMs + SAMPLING_PERIOD_MS);
         }
 
-        int getCurrentRotation() {
-            return SURFACE_ROTATIONS[mRotation];
+        int getCurrentRotation(int lastRotation) {
+            if (mTiltDistrust > 0) {
+                // we really don't know the current orientation, so trust what's currently displayed
+                mRotation = SURFACE_TO_INTERNAL_ROTATION[lastRotation];
+            }
+            return INTERNAL_TO_SURFACE_ROTATION[mRotation];
         }
 
         private void calculateNewRotation(float orientation, float tiltAngle) {
@@ -267,7 +277,7 @@
 
             if (localLOGV) Log.i(TAG, " new rotation = " + rotation);
             mRotation = rotation;
-            mOrientationListener.onOrientationChanged(getCurrentRotation());
+            mOrientationListener.onOrientationChanged(INTERNAL_TO_SURFACE_ROTATION[mRotation]);
         }
 
         private float lowpassFilter(float newValue, float oldValue, float alpha) {
@@ -306,7 +316,8 @@
             mTiltAngle = lowpassFilter(newTiltAngle, mTiltAngle, alpha);
 
             float absoluteTilt = Math.abs(mTiltAngle);
-            if (checkFullyTilted(absoluteTilt)) {
+            checkFullyTilted(absoluteTilt);
+            if (mTiltDistrust > 0) {
                 return; // when fully tilted, ignore orientation entirely
             }
 
@@ -347,11 +358,9 @@
          * get un-tilted.
          *
          * @param absoluteTilt the absolute value of the current tilt angle
-         * @return true if the phone is fully tilted
          */
-        private boolean checkFullyTilted(float absoluteTilt) {
-            boolean fullyTilted = absoluteTilt > MAX_TILT;
-            if (fullyTilted) {
+        private void checkFullyTilted(float absoluteTilt) {
+            if (absoluteTilt > MAX_TILT) {
                 if (mRotation == ROTATION_0) {
                     mOrientationAngle = 0;
                 } else if (mRotation == ROTATION_90) {
@@ -366,7 +375,6 @@
             } else if (mTiltDistrust > 0) {
                 mTiltDistrust--;
             }
-            return fullyTilted;
         }
 
         /**
@@ -389,8 +397,8 @@
          */
         private void filterOrientation(float absoluteTilt, float orientationAngle) {
             float alpha = DEFAULT_LOWPASS_ALPHA;
-            if (mTiltDistrust > 0 || mAccelerationDistrust > 1) {
-                // when fully tilted, or under more than a transient acceleration, distrust heavily
+            if (mAccelerationDistrust > 1) {
+                // when under more than a transient acceleration, distrust heavily
                 alpha = ACCELERATING_LOWPASS_ALPHA;
             } else if (absoluteTilt > PARTIAL_TILT || mAccelerationDistrust == 1) {
                 // when tilted partway, or under transient acceleration, distrust lightly
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index f3392d9..9a1e41c 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -307,7 +307,7 @@
      * animated as well as the objects parents. (This is to support animation
      * sizes being specifed relative to these dimensions.)
      *
-     * <p>Objects that interpret a Animations should call this method when
+     * <p>Objects that interpret Animations should call this method when
      * the sizes of the object being animated and its parent are known, and
      * before calling {@link #getTransformation}.
      *
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 8e4cf67..c090f8e 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -16,12 +16,6 @@
 
 package android.view.animation;
 
-import android.animation.Animatable;
-import android.animation.Animator;
-import android.animation.PropertyAnimator;
-import android.animation.Sequencer;
-import android.content.res.TypedArray;
-import android.util.TypedValue;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -33,7 +27,6 @@
 import android.os.SystemClock;
 
 import java.io.IOException;
-import java.util.ArrayList;
 
 /**
  * Defines common utilities for working with animations.
@@ -92,42 +85,6 @@
         }
     }
 
-    /**
-     * Loads an {@link Animation} object from a resource
-     *
-     * @param context Application context used to access resources
-     * @param id The resource id of the animation to load
-     * @return The animation object reference by the specified id
-     * @throws NotFoundException when the animation cannot be loaded
-     */
-    public static Animatable loadAnimator(Context context, int id)
-            throws NotFoundException {
-
-        XmlResourceParser parser = null;
-        try {
-            parser = context.getResources().getAnimation(id);
-            return createAnimatableFromXml(context, parser);
-        } catch (XmlPullParserException ex) {
-            NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
-                    Integer.toHexString(id));
-            rnf.initCause(ex);
-            throw rnf;
-        } catch (IOException ex) {
-            NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
-                    Integer.toHexString(id));
-            rnf.initCause(ex);
-            throw rnf;
-        } finally {
-            if (parser != null) parser.close();
-        }
-    }
-
-    private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser)
-            throws XmlPullParserException, IOException {
-
-        return createAnimatableFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
-    }
-
     private static Animation createAnimationFromXml(Context c, XmlPullParser parser)
             throws XmlPullParserException, IOException {
 
@@ -176,66 +133,6 @@
 
     }
 
-    private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser,
-            AttributeSet attrs, Sequencer parent, int sequenceOrdering)
-            throws XmlPullParserException, IOException {
-
-        Animatable anim = null;
-        ArrayList<Animatable> childAnims = null;
-
-        // Make sure we are on a start tag.
-        int type;
-        int depth = parser.getDepth();
-
-        while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
-               && type != XmlPullParser.END_DOCUMENT) {
-
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            String  name = parser.getName();
-
-            if (name.equals("property")) {
-                anim = new PropertyAnimator(c, attrs);
-            } else if (name.equals("animator")) {
-                anim = new Animator(c, attrs);
-            } else if (name.equals("sequencer")) {
-                anim = new Sequencer();
-                TypedArray a = c.obtainStyledAttributes(attrs,
-                        com.android.internal.R.styleable.Sequencer);
-                int ordering = a.getInt(com.android.internal.R.styleable.Sequencer_ordering,
-                        TOGETHER);
-                createAnimatableFromXml(c, parser, attrs, (Sequencer) anim,  ordering);
-                a.recycle();
-            } else {
-                throw new RuntimeException("Unknown animator name: " + parser.getName());
-            }
-
-            if (parent != null) {
-                if (childAnims == null) {
-                    childAnims = new ArrayList<Animatable>();
-                }
-                childAnims.add(anim);
-            }
-        }
-        if (parent != null && childAnims != null) {
-            Animatable[] animsArray = new Animatable[childAnims.size()];
-            int index = 0;
-            for (Animatable a : childAnims) {
-                animsArray[index++] = a;
-            }
-            if (sequenceOrdering == TOGETHER) {
-                parent.playTogether(animsArray);
-            } else {
-                parent.playSequentially(animsArray);
-            }
-        }
-
-        return anim;
-
-    }
-
     public static LayoutAnimationController loadLayoutAnimation(Context context, int id)
             throws NotFoundException {
         
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 6ac1633..3801948 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -84,9 +84,14 @@
             }
         }
     }
-    
+
     public static void setComposingSpans(Spannable text) {
-        final Object[] sps = text.getSpans(0, text.length(), Object.class);
+        setComposingSpans(text, 0, text.length());
+    }
+
+    /** @hide */
+    public static void setComposingSpans(Spannable text, int start, int end) {
+        final Object[] sps = text.getSpans(start, end, Object.class);
         if (sps != null) {
             for (int i=sps.length-1; i>=0; i--) {
                 final Object o = sps[i];
@@ -94,18 +99,19 @@
                     text.removeSpan(o);
                     continue;
                 }
+
                 final int fl = text.getSpanFlags(o);
                 if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK)) 
                         != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
                     text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
-                            (fl&Spanned.SPAN_POINT_MARK_MASK)
+                            (fl & ~Spanned.SPAN_POINT_MARK_MASK)
                                     | Spanned.SPAN_COMPOSING
                                     | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                 }
             }
         }
-        
-        text.setSpan(COMPOSING, 0, text.length(),
+
+        text.setSpan(COMPOSING, start, end,
                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
     }
     
@@ -312,6 +318,31 @@
     }
 
     /**
+     * The default implementation returns the text currently selected, or null if none is
+     * selected.
+     */
+    public CharSequence getSelectedText(int flags) {
+        final Editable content = getEditable();
+        if (content == null) return null;
+
+        int a = Selection.getSelectionStart(content);
+        int b = Selection.getSelectionEnd(content);
+
+        if (a > b) {
+            int tmp = a;
+            a = b;
+            b = tmp;
+        }
+
+        if (a == b) return null;
+
+        if ((flags&GET_TEXT_WITH_STYLES) != 0) {
+            return content.subSequence(a, b);
+        }
+        return TextUtils.substring(content, a, b);
+    }
+
+    /**
      * The default implementation returns the given amount of text from the
      * current cursor position in the buffer.
      */
@@ -385,6 +416,38 @@
         return true;
     }
 
+    public boolean setComposingRegion(int start, int end) {
+        final Editable content = getEditable();
+        if (content != null) {
+            beginBatchEdit();
+            removeComposingSpans(content);
+            int a = start;
+            int b = end;
+            if (a > b) {
+                int tmp = a;
+                a = b;
+                b = tmp;
+            }
+            if (a < 0) a = 0;
+            if (b > content.length()) b = content.length();
+
+            ensureDefaultComposingSpans();
+            if (mDefaultComposingSpans != null) {
+                for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
+                    content.setSpan(mDefaultComposingSpans[i], a, b,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
+                }
+            }
+
+            content.setSpan(COMPOSING, a, b,
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
+
+            endBatchEdit();
+            sendCurrentText();
+        }
+        return true;
+    }
+
     /**
      * The default implementation changes the selection position in the
      * current editable text.
@@ -479,7 +542,32 @@
             content.clear();
         }
     }
-    
+
+    private void ensureDefaultComposingSpans() {
+        if (mDefaultComposingSpans == null) {
+            Context context;
+            if (mTargetView != null) {
+                context = mTargetView.getContext();
+            } else if (mIMM.mServedView != null) {
+                context = mIMM.mServedView.getContext();
+            } else {
+                context = null;
+            }
+            if (context != null) {
+                TypedArray ta = context.getTheme()
+                        .obtainStyledAttributes(new int[] {
+                                com.android.internal.R.attr.candidatesTextStyleSpans
+                        });
+                CharSequence style = ta.getText(0);
+                ta.recycle();
+                if (style != null && style instanceof Spanned) {
+                    mDefaultComposingSpans = ((Spanned)style).getSpans(
+                            0, style.length(), Object.class);
+                }
+            }
+        }
+    }
+
     private void replaceText(CharSequence text, int newCursorPosition,
             boolean composing) {
         final Editable content = getEditable();
@@ -520,32 +608,11 @@
             if (!(text instanceof Spannable)) {
                 sp = new SpannableStringBuilder(text);
                 text = sp;
-                if (mDefaultComposingSpans == null) {
-                    Context context;
-                    if (mTargetView != null) {
-                        context = mTargetView.getContext();
-                    } else if (mIMM.mServedView != null) {
-                        context = mIMM.mServedView.getContext();
-                    } else {
-                        context = null;
-                    }
-                    if (context != null) {
-                        TypedArray ta = context.getTheme()
-                                .obtainStyledAttributes(new int[] {
-                                        com.android.internal.R.attr.candidatesTextStyleSpans
-                                });
-                        CharSequence style = ta.getText(0);
-                        ta.recycle();
-                        if (style != null && style instanceof Spanned) {
-                            mDefaultComposingSpans = ((Spanned)style).getSpans(
-                                    0, style.length(), Object.class);
-                        }
-                    }
-                }
+                ensureDefaultComposingSpans();
                 if (mDefaultComposingSpans != null) {
                     for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
                         sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(),
-                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
                     }
                 }
             } else {
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 8b6831e..3b8a364 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -80,6 +80,21 @@
     public CharSequence getTextAfterCursor(int n, int flags);
 
     /**
+     * Gets the selected text, if any.
+     *
+     * <p>This method may fail if either the input connection has become
+     * invalid (such as its process crashing) or the client is taking too
+     * long to respond with the text (it is given a couple of seconds to return).
+     * In either case, a null is returned.
+     *
+     * @param flags Supplies additional options controlling how the text is
+     * returned.  May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
+     * @return Returns the text that is currently selected, if any, or null if
+     * no text is selected.
+     */
+    public CharSequence getSelectedText(int flags);
+
+    /**
      * Retrieve the current capitalization mode in effect at the current
      * cursor position in the text.  See
      * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode} for
@@ -162,6 +177,18 @@
     public boolean setComposingText(CharSequence text, int newCursorPosition);
 
     /**
+     * Mark a certain region of text as composing text. Any composing text set
+     * previously will be removed automatically. The default style for composing
+     * text is used.
+     *
+     * @param start the position in the text at which the composing region begins
+     * @param end the position in the text at which the composing region ends
+     * @return Returns true on success, false if the input connection is no longer
+     * valid.
+     */
+    public boolean setComposingRegion(int start, int end);
+
+    /**
      * Have the text editor finish whatever composing text is currently
      * active.  This simply leaves the text as-is, removing any special
      * composing styling or other state that was around it.  The cursor
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 210559a..b73f9bb 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -50,6 +50,10 @@
         return mTarget.getTextAfterCursor(n, flags);
     }
 
+    public CharSequence getSelectedText(int flags) {
+        return mTarget.getSelectedText(flags);
+    }
+
     public int getCursorCapsMode(int reqModes) {
         return mTarget.getCursorCapsMode(reqModes);
     }
@@ -67,6 +71,10 @@
         return mTarget.setComposingText(text, newCursorPosition);
     }
 
+    public boolean setComposingRegion(int start, int end) {
+        return mTarget.setComposingRegion(start, end);
+    }
+
     public boolean finishComposingText() {
         return mTarget.finishComposingText();
     }
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index b021ded..e972c24 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -38,6 +38,8 @@
 
 import junit.framework.Assert;
 
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
 import java.net.URLEncoder;
@@ -658,6 +660,96 @@
     }
 
     /**
+     * Called by JNI.
+     * Read from an InputStream into a supplied byte[]
+     * This method catches any exceptions so they don't crash the JVM.
+     * @param inputStream InputStream to read from.
+     * @param output Bytearray that gets the output.
+     * @return the number of bytes read, or -i if then end of stream has been reached
+     */
+    private static int readFromStream(InputStream inputStream, byte[] output) {
+        try {
+            return inputStream.read(output);
+        } catch(java.io.IOException e) {
+            // If we get an exception, return end of stream
+            return -1;
+        }
+    }
+
+    /**
+     * Get the InputStream for an Android resource
+     * There are three different kinds of android resources:
+     * - file:///android_res
+     * - file:///android_asset
+     * - content://
+     * @param url The url to load.
+     * @return An InputStream to the android resource
+     */
+    private InputStream inputStreamForAndroidResource(String url, int type) {
+        final int RESOURCE = 1;
+        final int ASSET = 2;
+        final int CONTENT = 3;
+
+        if (type == RESOURCE) {
+            // file:///android_res
+            if (url == null || url.length() == 0) {
+                Log.e(LOGTAG, "url has length 0 " + url);
+                return null;
+            }
+            int slash = url.indexOf('/');
+            int dot = url.indexOf('.', slash);
+            if (slash == -1 || dot == -1) {
+                Log.e(LOGTAG, "Incorrect res path: " + url);
+                return null;
+            }
+            String subClassName = url.substring(0, slash);
+            String fieldName = url.substring(slash + 1, dot);
+            String errorMsg = null;
+            try {
+                final Class<?> d = mContext.getApplicationContext()
+                        .getClassLoader().loadClass(
+                                mContext.getPackageName() + ".R$"
+                                        + subClassName);
+                final java.lang.reflect.Field field = d.getField(fieldName);
+                final int id = field.getInt(null);
+                TypedValue value = new TypedValue();
+                mContext.getResources().getValue(id, value, true);
+                if (value.type == TypedValue.TYPE_STRING) {
+                    return mContext.getAssets().openNonAsset(
+                            value.assetCookie, value.string.toString(),
+                            AssetManager.ACCESS_STREAMING);
+                } else {
+                    // Old stack only supports TYPE_STRING for res files
+                    Log.e(LOGTAG, "not of type string: " + url);
+                    return null;
+                }
+            } catch (Exception e) {
+                Log.e(LOGTAG, "Exception: " + url);
+                return null;
+            }
+
+        } else if (type == ASSET) {
+            // file:///android_asset
+            try {
+                AssetManager assets = mContext.getAssets();
+                return assets.open(url, AssetManager.ACCESS_STREAMING);
+            } catch (IOException e) {
+                return null;
+            }
+        } else if (type == CONTENT) {
+            try {
+                Uri uri = Uri.parse(url);
+                return mContext.getContentResolver().openInputStream(uri);
+            } catch (Exception e) {
+                Log.e(LOGTAG, "Exception: " + url);
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Start loading a resource.
      * @param loaderHandle The native ResourceLoader that is the target of the
      *                     data.
diff --git a/core/java/android/webkit/DeviceOrientationService.java b/core/java/android/webkit/DeviceOrientationService.java
index 07d3d2f..4ff849e 100755
--- a/core/java/android/webkit/DeviceOrientationService.java
+++ b/core/java/android/webkit/DeviceOrientationService.java
@@ -16,19 +16,39 @@
 
 package android.webkit;
 
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.os.Handler;
 import android.webkit.DeviceOrientationManager;
 import java.lang.Runnable;
+import java.util.List;
 
 
-final class DeviceOrientationService {
+final class DeviceOrientationService implements SensorEventListener {
+    // The gravity vector expressed in the body frame.
+    private float[] mGravityVector;
+    // The geomagnetic vector expressed in the body frame.
+    private float[] mMagneticFieldVector;
+
     private DeviceOrientationManager mManager;
     private boolean mIsRunning;
     private Handler mHandler;
+    private SensorManager mSensorManager;
+    private Context mContext;
+    private Double mAlpha;
+    private Double mBeta;
+    private Double mGamma;
 
-    public DeviceOrientationService(DeviceOrientationManager manager) {
+    private static final double DELTA_DEGRESS = 1.0;
+
+    public DeviceOrientationService(DeviceOrientationManager manager, Context context) {
         mManager = manager;
         assert(mManager != null);
+        mContext = context;
+        assert(mContext != null);
      }
 
     public void start() {
@@ -55,9 +75,6 @@
 
     private void sendErrorEvent() {
         assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-        if (mHandler == null) {
-            mHandler = new Handler();
-        }
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -70,12 +87,128 @@
     }
 
     private void registerForSensors() {
-        // Send the error event for now.
-        // FIXME: Implement.
-        sendErrorEvent();
+        if (mHandler == null) {
+            mHandler = new Handler();
+        }
+        if (!registerForAccelerometerSensor() || !registerForMagneticFieldSensor()) {
+            unregisterFromSensors();
+            sendErrorEvent();
+        }
+    }
+
+    private void getOrientationUsingGetRotationMatrix() {
+        if (mGravityVector == null || mMagneticFieldVector == null) {
+            return;
+        }
+
+        // Get the rotation matrix.
+        // The rotation matrix that transforms from the body frame to the earth frame.
+        float[] deviceRotationMatrix = new float[9];
+        if (!SensorManager.getRotationMatrix(
+                deviceRotationMatrix, null, mGravityVector, mMagneticFieldVector)) {
+            return;
+        }
+
+        // Convert rotation matrix to rotation angles.
+        // Assuming that the rotations are appied in the order listed at
+        // http://developer.android.com/reference/android/hardware/SensorEvent.html#values
+        // the rotations are applied about the same axes and in the same order as required by the
+        // API. The only conversions are sign changes as follows.
+        // The angles are in radians
+        float[] rotationAngles = new float[3];
+        SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
+        double alpha = Math.toDegrees(-rotationAngles[0]) - 90.0;
+        while (alpha < 0.0) { alpha += 360.0; } // [0, 360)
+        double beta = Math.toDegrees(-rotationAngles[1]);
+        while (beta < -180.0) { beta += 360.0; } // [-180, 180)
+        double gamma = Math.toDegrees(rotationAngles[2]);
+        while (gamma < -90.0) { gamma += 360.0; } // [-90, 90)
+
+        maybeSendChange(alpha, beta, gamma);
+    }
+
+    private SensorManager getSensorManager() {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+        if (mSensorManager == null) {
+            mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+        }
+        return mSensorManager;
+    }
+
+    private boolean registerForAccelerometerSensor() {
+        List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_ACCELEROMETER);
+        if (sensors.isEmpty()) {
+            return false;
+        }
+        // TODO: Consider handling multiple sensors.
+        return getSensorManager().registerListener(
+                this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler);
+    }
+
+    private boolean registerForMagneticFieldSensor() {
+        List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
+        if (sensors.isEmpty()) {
+            return false;
+        }
+        // TODO: Consider handling multiple sensors.
+        return getSensorManager().registerListener(
+                this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler);
     }
 
     private void unregisterFromSensors() {
-        // FIXME: Implement.
+        getSensorManager().unregisterListener(this);
+    }
+
+    private void maybeSendChange(double alpha, double beta, double gamma) {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+        if (mAlpha == null || mBeta == null || mGamma == null
+                || Math.abs(alpha - mAlpha) > DELTA_DEGRESS
+                || Math.abs(beta - mBeta) > DELTA_DEGRESS
+                || Math.abs(gamma - mGamma) > DELTA_DEGRESS) {
+            mAlpha = alpha;
+            mBeta = beta;
+            mGamma = gamma;
+            mManager.onOrientationChange(mAlpha, mBeta, mGamma);
+        }
+    }
+
+    /**
+     * SensorEventListener implementation.
+     * Callbacks happen on the thread on which we registered - the WebCore thread.
+     */
+    public void onSensorChanged(SensorEvent event) {
+        assert(event.values.length == 3);
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+
+        if (!mIsRunning) {
+            return;
+        }
+
+        switch (event.sensor.getType()) {
+          case Sensor.TYPE_ACCELEROMETER:
+            if (mGravityVector == null) {
+                mGravityVector = new float[3];
+            }
+            mGravityVector[0] = event.values[0];
+            mGravityVector[1] = event.values[1];
+            mGravityVector[2] = event.values[2];
+            getOrientationUsingGetRotationMatrix();
+            break;
+          case Sensor.TYPE_MAGNETIC_FIELD:
+            if (mMagneticFieldVector == null) {
+                mMagneticFieldVector = new float[3];
+            }
+            mMagneticFieldVector[0] = event.values[0];
+            mMagneticFieldVector[1] = event.values[1];
+            mMagneticFieldVector[2] = event.values[2];
+            getOrientationUsingGetRotationMatrix();
+            break;
+          default:
+            assert(false);
+        }
+    }
+
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
     }
 }
diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java
index cdcb662..f7d1134 100644
--- a/core/java/android/webkit/PluginManager.java
+++ b/core/java/android/webkit/PluginManager.java
@@ -149,41 +149,11 @@
                     continue;
                 }
 
-                // check if the plugin has the required permissions
-                String permissions[] = pkgInfo.requestedPermissions;
-                if (permissions == null) {
+                // check if the plugin has the required permissions and
+                // signatures
+                if (!containsPluginPermissionAndSignatures(pkgInfo)) {
                     continue;
                 }
-                boolean permissionOk = false;
-                for (String permit : permissions) {
-                    if (PLUGIN_PERMISSION.equals(permit)) {
-                        permissionOk = true;
-                        break;
-                    }
-                }
-                if (!permissionOk) {
-                    continue;
-                }
-
-                // check to ensure the plugin is properly signed
-                Signature signatures[] = pkgInfo.signatures;
-                if (signatures == null) {
-                    continue;
-                }
-                if (SystemProperties.getBoolean("ro.secure", false)) {
-                    boolean signatureMatch = false;
-                    for (Signature signature : signatures) {
-                        for (int i = 0; i < SIGNATURES.length; i++) {
-                            if (SIGNATURES[i].equals(signature)) {
-                                signatureMatch = true;
-                                break;
-                            }
-                        }
-                    }
-                    if (!signatureMatch) {
-                        continue;
-                    }
-                }
 
                 // determine the type of plugin from the manifest
                 if (serviceInfo.metaData == null) {
@@ -226,6 +196,64 @@
     }
 
     /* package */
+    boolean containsPluginPermissionAndSignatures(String pluginAPKName) {
+        PackageManager pm = mContext.getPackageManager();
+
+        // retrieve information from the plugin's manifest
+        try {
+            PackageInfo pkgInfo = pm.getPackageInfo(pluginAPKName, PackageManager.GET_PERMISSIONS
+                    | PackageManager.GET_SIGNATURES);
+            if (pkgInfo != null) {
+                return containsPluginPermissionAndSignatures(pkgInfo);
+            }
+        } catch (NameNotFoundException e) {
+            Log.w(LOGTAG, "Can't find plugin: " + pluginAPKName);
+        }
+        return false;
+    }
+
+    private static boolean containsPluginPermissionAndSignatures(PackageInfo pkgInfo) {
+
+        // check if the plugin has the required permissions
+        String permissions[] = pkgInfo.requestedPermissions;
+        if (permissions == null) {
+            return false;
+        }
+        boolean permissionOk = false;
+        for (String permit : permissions) {
+            if (PLUGIN_PERMISSION.equals(permit)) {
+                permissionOk = true;
+                break;
+            }
+        }
+        if (!permissionOk) {
+            return false;
+        }
+
+        // check to ensure the plugin is properly signed
+        Signature signatures[] = pkgInfo.signatures;
+        if (signatures == null) {
+            return false;
+        }
+        if (SystemProperties.getBoolean("ro.secure", false)) {
+            boolean signatureMatch = false;
+            for (Signature signature : signatures) {
+                for (int i = 0; i < SIGNATURES.length; i++) {
+                    if (SIGNATURES[i].equals(signature)) {
+                        signatureMatch = true;
+                        break;
+                    }
+                }
+            }
+            if (!signatureMatch) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /* package */
     String getPluginsAPKName(String pluginLib) {
 
         // basic error checking on input params
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a2628ec..9597d8c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -18,10 +18,14 @@
 
 import android.annotation.Widget;
 import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
 import android.content.ClipboardManager;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.IntentFilter;
 import android.content.DialogInterface.OnCancelListener;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.Intent;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
@@ -40,6 +44,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.net.http.SslCertificate;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -92,6 +97,7 @@
 import java.net.URLDecoder;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -111,9 +117,12 @@
  * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
  *
  * <p>This must be a child of the <a
- * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code &lt;manifest&gt;}</a>
+ * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
  * element.</p>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-webview.html">Web View
+ * tutorial</a>.</p>
+ *
  * <h3>Basic usage</h3>
  *
  * <p>By default, a WebView provides no browser-like widgets, does not
@@ -131,7 +140,7 @@
  * </pre>
  * <p>See {@link android.content.Intent} for more information.</p>
  *
- * <p>To provide a WebView in your own Activity, include a {@code &lt;WebView&gt;} in your layout,
+ * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
  * or set the entire Activity window as a WebView during {@link
  * android.app.Activity#onCreate(Bundle) onCreate()}:</p>
  * <pre class="prettyprint">
@@ -278,7 +287,7 @@
  * low density screens scale down. This is also the default behavior.</li>
  * <li>{@code low-dpi} - Use ldpi as the target dpi. Medium and high density screens scale up
  * as appropriate.</li>
- * <li><em>{@code &lt;value&gt;}</em> - Specify a dpi value to use as the target dpi (accepted
+ * <li><em>{@code <value>}</em> - Specify a dpi value to use as the target dpi (accepted
  * values are 70-400).</li>
  * </ul>
  * <p>Here's an example meta tag to specify the target density:</p>
@@ -874,6 +883,7 @@
          * such as the mZoomManager.
          */
         init();
+        setupPackageListener(context);
         updateMultiTouchSupport(context);
 
         if (privateBrowsing) {
@@ -881,6 +891,96 @@
         }
     }
 
+    /*
+     * The intent receiver that monitors for changes to relevant packages (e.g.,
+     * sGoogleApps) and notifies WebViewCore of their existence.
+     */
+    private static BroadcastReceiver sPackageInstallationReceiver = null;
+
+    /*
+     * A set of Google packages we monitor for the
+     * navigator.isApplicationInstalled() API. Add additional packages as
+     * needed.
+     */
+    private static Set<String> sGoogleApps;
+    static {
+        sGoogleApps = new HashSet<String>();
+        sGoogleApps.add("com.google.android.youtube");
+    }
+
+    private void setupPackageListener(Context context) {
+
+        /*
+         * we must synchronize the instance check and the creation of the
+         * receiver to ensure that only ONE receiver exists for all WebView
+         * instances.
+         */
+        synchronized (WebView.class) {
+
+            // if the receiver already exists then we do not need to register it
+            // again
+            if (sPackageInstallationReceiver != null) {
+                return;
+            }
+
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addDataScheme("package");
+            sPackageInstallationReceiver = new BroadcastReceiver() {
+
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    final String action = intent.getAction();
+                    final String packageName = intent.getData().getSchemeSpecificPart();
+                    final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                    if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
+                        // if it is replacing, refreshPlugins() when adding
+                        return;
+                    }
+
+                    if (sGoogleApps.contains(packageName)) {
+                        if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                            mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAME, packageName);
+                        } else {
+                            mWebViewCore.sendMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
+                        }
+                    }
+
+                    PluginManager pm = PluginManager.getInstance(context);
+                    if (pm.containsPluginPermissionAndSignatures(packageName)) {
+                        pm.refreshPlugins(Intent.ACTION_PACKAGE_ADDED.equals(action));
+                    }
+                }
+            };
+
+            context.getApplicationContext().registerReceiver(sPackageInstallationReceiver, filter);
+        }
+
+        // check if any of the monitored apps are already installed
+        AsyncTask<Void, Void, Set<String>> task = new AsyncTask<Void, Void, Set<String>>() {
+
+            @Override
+            protected Set<String> doInBackground(Void... unused) {
+                Set<String> installedPackages = new HashSet<String>();
+                PackageManager pm = mContext.getPackageManager();
+                List<PackageInfo> packages = pm.getInstalledPackages(0);
+                for (PackageInfo p : packages) {
+                    if (sGoogleApps.contains(p.packageName)) {
+                        installedPackages.add(p.packageName);
+                    }
+                }
+                return installedPackages;
+            }
+
+            // Executes on the UI thread
+            @Override
+            protected void onPostExecute(Set<String> installedPackages) {
+                mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, installedPackages);
+            }
+        };
+        task.execute();
+    }
+
     void updateMultiTouchSupport(Context context) {
         mZoomManager.updateMultiTouchSupport(context);
     }
@@ -1275,6 +1375,8 @@
             outState.putBundle("certificate",
                                SslCertificate.saveState(mCertificate));
         }
+        outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
+        mZoomManager.saveZoomState(outState);
         return list;
     }
 
@@ -1437,6 +1539,11 @@
                 // Update the copy to have the correct index.
                 returnList.setCurrentIndex(index);
             }
+            // Restore private browsing setting.
+            if (inState.getBoolean("privateBrowsingEnabled")) {
+                getSettings().setPrivateBrowsingEnabled(true);
+            }
+            mZoomManager.restoreZoomState(inState);
             // Remove all pending messages because we are restoring previous
             // state.
             mWebViewCore.removeMessages();
@@ -1699,18 +1806,7 @@
         getSettings().setPrivateBrowsingEnabled(true);
 
         if (!wasPrivateBrowsingEnabled) {
-            StringBuilder data = new StringBuilder(1024);
-            try {
-                InputStreamReader file = new InputStreamReader(mContext.getResources().openRawResource(com.android.internal.R.raw.incognito_mode_start_page));
-                int size;
-                char[] buffer = new char[1024];
-                while ((size = file.read(buffer)) != -1) {
-                    data.append(buffer, 0, size);
-                }
-            } catch (IOException e) {
-                // This should never happen since this is a static resource.
-            }
-            loadDataWithBaseURL(null, data.toString(), "text/html", "utf-8", null);
+            loadUrl("browser:incognito");
         }
     }
 
@@ -3079,45 +3175,6 @@
         return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
     }
 
-    /**
-     * Use this method to inform the webview about packages that are installed
-     * in the system. This information will be used by the
-     * navigator.isApplicationInstalled() API.
-     * @param packageNames is a set of package names that are known to be
-     * installed in the system.
-     *
-     * @hide not a public API
-     */
-    public void addPackageNames(Set<String> packageNames) {
-        mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, packageNames);
-    }
-
-    /**
-     * Use this method to inform the webview about single packages that are
-     * installed in the system. This information will be used by the
-     * navigator.isApplicationInstalled() API.
-     * @param packageName is the name of a package that is known to be
-     * installed in the system.
-     *
-     * @hide not a public API
-     */
-    public void addPackageName(String packageName) {
-        mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAME, packageName);
-    }
-
-    /**
-     * Use this method to inform the webview about packages that are uninstalled
-     * in the system. This information will be used by the
-     * navigator.isApplicationInstalled() API.
-     * @param packageName is the name of a package that has been uninstalled in
-     * the system.
-     *
-     * @hide not a public API
-     */
-    public void removePackageName(String packageName) {
-        mWebViewCore.sendMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
-    }
-
    /**
     * Return the list of currently loaded plugins.
     * @return The list of currently loaded plugins.
@@ -3300,7 +3357,8 @@
         setUpSelect();
         if (mNativeClass != 0 && nativeWordSelection(x, y)) {
             nativeSetExtendSelection();
-            getWebChromeClient().onSelectionStart(this);
+            WebChromeClient client = getWebChromeClient();
+            if (client != null) client.onSelectionStart(this);
             return true;
         }
         notifySelectDialogDismissed();
@@ -4033,7 +4091,7 @@
                 return true;
             }
             clearTextEntry();
-            nativeSetFollowedLink(true);
+            nativeShowCursorTimed();
             if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
                 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
                         nativeCursorNodePointer());
@@ -4120,7 +4178,8 @@
      */
     public void selectionDone() {
         if (mSelectingText) {
-            getWebChromeClient().onSelectionDone(this);
+            WebChromeClient client = getWebChromeClient();
+            if (client != null) client.onSelectionDone(this);
             invalidate(); // redraw without selection
             notifySelectDialogDismissed();
         }
@@ -7272,9 +7331,9 @@
     private native void     nativeSetExtendSelection();
     private native void     nativeSetFindIsEmpty();
     private native void     nativeSetFindIsUp(boolean isUp);
-    private native void     nativeSetFollowedLink(boolean followed);
     private native void     nativeSetHeightCanMeasure(boolean measure);
     private native void     nativeSetBaseLayer(int layer);
+    private native void     nativeShowCursorTimed();
     private native void     nativeReplaceBaseContent(int content);
     private native void     nativeCopyBaseContentToPicture(Picture pict);
     private native boolean  nativeHasContent();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 860edf29..fad4323 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -441,6 +441,8 @@
      */
     private native void nativeClearContent();
 
+    private native void nativeContentInvalidateAll();
+
     /**
      * Redraw a portion of the picture set. The Point wh returns the
      * width and height of the overall picture.
@@ -1880,10 +1882,10 @@
 
             synchronized (core) {
                 core.mDrawIsPaused = false;
-                if (core.mDrawIsScheduled) {
-                    core.mDrawIsScheduled = false;
-                    core.contentDraw();
-                }
+                // always redraw on resume to reenable gif animations
+                core.mDrawIsScheduled = false;
+                core.nativeContentInvalidateAll();
+                core.contentDraw();
             }
         }
     }
@@ -2504,7 +2506,8 @@
 
     protected DeviceOrientationService getDeviceOrientationService() {
         if (mDeviceOrientationService == null) {
-            mDeviceOrientationService = new DeviceOrientationService(mDeviceOrientationManager);
+            mDeviceOrientationService =
+                    new DeviceOrientationService(mDeviceOrientationManager, mContext);
         }
         return mDeviceOrientationService;
     }
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 10a8729..e20fee5 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -228,7 +228,6 @@
         super(context, attrs, defStyle);
     }
 
-
     /**
      * Interface definition for a callback to be invoked when an item in this
      * AdapterView has been clicked.
@@ -629,6 +628,7 @@
     /**
      * Sets the view to show if the adapter is empty
      */
+    @android.view.RemotableViewMethod
     public void setEmptyView(View emptyView) {
         mEmptyView = emptyView;
 
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index b6c8e47..0636d72 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcel;
@@ -323,15 +324,13 @@
         return null;
     }
 
-    private LayoutParams createOrReuseLayoutParams(View v) {
+    LayoutParams createOrReuseLayoutParams(View v) {
         final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
-        if (currentLp instanceof LayoutParams) {
+        if (currentLp instanceof ViewGroup.LayoutParams) {
             LayoutParams lp = (LayoutParams) currentLp;
-            lp.setHorizontalOffset(0);
-            lp.setVerticalOffset(0);
             return lp;
         }
-        return new LayoutParams(v);
+        return new ViewGroup.LayoutParams(0, 0);
     }
 
     void showOnly(int childIndex, boolean animate, boolean onLayout) {
@@ -472,10 +471,9 @@
 
             int childRight = mPaddingLeft + child.getMeasuredWidth();
             int childBottom = mPaddingTop + child.getMeasuredHeight();
-            LayoutParams lp = (LayoutParams) child.getLayoutParams();
 
-            child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
-                    childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
+            child.layout(mPaddingLeft, mPaddingTop,
+                    childRight, childBottom);
         }
         mDataChanged = false;
     }
@@ -690,6 +688,7 @@
         }
 
         mAdapter = adapter;
+        checkFocus();
 
         if (mAdapter != null) {
             mDataSetObserver = new AdapterDataSetObserver();
@@ -738,102 +737,4 @@
             setAdapter(mRemoteViewsAdapter);
         }
     }
-
-    private final Rect dirtyRect = new Rect();
-    @Override
-    public void removeViewInLayout(View view) {
-        // TODO: need to investigate this block a bit more
-        // and perhaps fix some other invalidations issues.
-        View parent = null;
-        view.setVisibility(INVISIBLE);
-        if (view.getLayoutParams() instanceof LayoutParams) {
-            LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            parent = lp.getParentAndDirtyRegion(dirtyRect);
-        }
-
-        super.removeViewInLayout(view);
-
-        if (parent != null)
-            parent.invalidate(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
-    }
-
-    static class LayoutParams extends ViewGroup.LayoutParams {
-        int horizontalOffset;
-        int verticalOffset;
-        View mView;
-
-        LayoutParams(View view) {
-            super(0, 0);
-            horizontalOffset = 0;
-            verticalOffset = 0;
-            mView = view;
-        }
-
-        LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-            horizontalOffset = 0;
-            verticalOffset = 0;
-        }
-
-        private Rect parentRect = new Rect();
-        void invalidateGlobalRegion(View v, Rect r) {
-            View p = v;
-            boolean firstPass = true;
-            parentRect.set(0, 0, 0, 0);
-            while (p.getParent() != null && p.getParent() instanceof View
-                    && !parentRect.contains(r)) {
-                if (!firstPass) r.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
-                firstPass = false;
-                p = (View) p.getParent();
-                parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(),
-                        p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY());
-            }
-            p.invalidate(r.left, r.top, r.right, r.bottom);
-        }
-
-        public View getParentAndDirtyRegion(Rect globalRect) {
-            globalRect.set(mView.getLeft(), mView.getTop(), mView.getRight(), mView.getBottom());
-            View p = mView;
-            boolean firstPass = true;
-            parentRect.set(0, 0, 0, 0);
-            while (p.getParent() != null && p.getParent() instanceof View
-                    && !parentRect.contains(globalRect)) {
-                if (!firstPass) {
-                    globalRect.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
-                }
-
-                firstPass = false;
-                p = (View) p.getParent();
-                parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(),
-                        p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY());
-            }
-            return p;
-        }
-
-        private Rect invalidateRect = new Rect();
-        // This is public so that PropertyAnimator can access it
-        public void setVerticalOffset(int newVerticalOffset) {
-            int offsetDelta = newVerticalOffset - verticalOffset;
-            verticalOffset = newVerticalOffset;
-            if (mView != null) {
-                mView.requestLayout();
-                int top = Math.min(mView.getTop() + offsetDelta, mView.getTop());
-                int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom());
-                invalidateRect.set(mView.getLeft(), top, mView.getRight(), bottom);
-                invalidateGlobalRegion(mView, invalidateRect);
-            }
-        }
-
-        public void setHorizontalOffset(int newHorizontalOffset) {
-            int offsetDelta = newHorizontalOffset - horizontalOffset;
-            horizontalOffset = newHorizontalOffset;
-            if (mView != null) {
-                mView.requestLayout();
-                int left = Math.min(mView.getLeft() + offsetDelta, mView.getLeft());
-                int right = Math.max(mView.getRight() + offsetDelta, mView.getRight());
-                invalidateRect.set(left, mView.getTop(), right, mView.getBottom());
-                invalidateGlobalRegion(mView, invalidateRect);
-            }
-        }
-    }
 }
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 34aef99..e07befa 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -75,6 +75,9 @@
  * }
  * </pre>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-autocomplete.html">Auto Complete
+ * tutorial</a>.</p>
+ *
  * @attr ref android.R.styleable#AutoCompleteTextView_completionHint
  * @attr ref android.R.styleable#AutoCompleteTextView_completionThreshold
  * @attr ref android.R.styleable#AutoCompleteTextView_completionHintView
diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java
index 5e692d4..176233e 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -48,6 +48,9 @@
  * }
  * </pre>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
  * <p><strong>XML attributes</strong></p>
  * <p> 
  * See {@link android.R.styleable#Button Button Attributes}, 
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index ff63a24..b89c2a9 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -41,6 +41,9 @@
  *     }
  * }
  * </pre>
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
  *  
  * <p><strong>XML attributes</strong></p> 
  * <p>
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 1fc23ab..8aed454a4 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -37,6 +37,9 @@
 /**
  * A view for selecting a month / year / day based on a calendar like layout.
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
+ *
  * For a dialog using this view, see {@link android.app.DatePickerDialog}.
  */
 @Widget
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 1532db1..0da68a4 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -35,6 +35,9 @@
 /**
  * EditText is a thin veneer over TextView that configures itself
  * to be editable.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
  * <p>
  * <b>XML attributes</b>
  * <p>
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index c47292f..9789658 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -46,6 +46,9 @@
  * <p>
  * Views given to the Gallery should use {@link Gallery.LayoutParams} as their
  * layout parameters type.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-gallery.html">Gallery
+ * tutorial</a>.</p>
  * 
  * @attr ref android.R.styleable#Gallery_animationDuration
  * @attr ref android.R.styleable#Gallery_spacing
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index f70051d..dffe685 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -34,6 +34,9 @@
 /**
  * A view that shows items in two-dimensional scrolling grid. The items in the
  * grid come from the {@link ListAdapter} associated with this view.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-gridview.html">Grid
+ * View tutorial</a>.</p>
  */
 @RemoteView
 public class GridView extends AbsListView {
diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java
index 5c05170..d680fad 100644
--- a/core/java/android/widget/ImageButton.java
+++ b/core/java/android/widget/ImageButton.java
@@ -32,7 +32,7 @@
  * {@link android.widget.Button}, with the standard button background
  * that changes color during different button states. The image on the surface
  * of the button is defined either by the {@code android:src} attribute in the
- * {@code &lt;ImageButton&gt;} XML element or by the 
+ * {@code <ImageButton>} XML element or by the
  * {@link #setImageResource(int)} method.</p>
  * 
  * <p>To remove the standard button background image, define your own 
@@ -57,11 +57,14 @@
  * based on the state of the button and the corresponding images
  * defined in the XML.</p>
  *
- * <p>The order of the {@code &lt;item>} elements is important because they are
+ * <p>The order of the {@code <item>} elements is important because they are
  * evaluated in order. This is why the "normal" button image comes last, because
  * it will only be applied after {@code android:state_pressed} and {@code
  * android:state_focused} have both evaluated false.</p>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
  * <p><strong>XML attributes</strong></p>
  * <p>
  * See {@link android.R.styleable#ImageView Button Attributes},
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 53187bf..1e5489a 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -37,6 +37,9 @@
  * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}.
  * The default orientation is horizontal.
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-linearlayout.html">Linear Layout
+ * tutorial</a>.</p>
+ *
  * <p>
  * Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams}
  * for layout attributes </p>
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 35e0603..12ff292 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -604,6 +604,15 @@
         mDropDownList = null;
     }
 
+    /**
+     * Set a listener to receive a callback when the popup is dismissed.
+     *
+     * @param listener Listener that will be notified when the popup is dismissed.
+     */
+    public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
+        mPopup.setOnDismissListener(listener);
+    }
+
     private void removePromptView() {
         if (mPromptView != null) {
             final ViewParent parent = mPromptView.getParent();
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index de7157b..f9bdc43 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -57,6 +57,9 @@
  * A view that shows items in a vertically scrolling list. The items
  * come from the {@link ListAdapter} associated with this view.
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-listview.html">List View
+ * tutorial</a>.</p>
+ *
  * @attr ref android.R.styleable#ListView_entries
  * @attr ref android.R.styleable#ListView_divider
  * @attr ref android.R.styleable#ListView_dividerHeight
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 0f52fc8..3a4487c 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -21,8 +21,9 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
-import android.graphics.Shader;
 import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.drawable.Animatable;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ClipDrawable;
@@ -30,11 +31,14 @@
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.StateListDrawable;
-import android.graphics.drawable.Animatable;
 import android.graphics.drawable.shapes.RoundRectShape;
 import android.graphics.drawable.shapes.Shape;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.view.Gravity;
+import android.view.RemotableViewMethod;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.animation.AlphaAnimation;
@@ -44,9 +48,6 @@
 import android.view.animation.LinearInterpolator;
 import android.view.animation.Transformation;
 import android.widget.RemoteViews.RemoteView;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
 
 import com.android.internal.R;
 
@@ -763,6 +764,7 @@
     }
 
     @Override
+    @RemotableViewMethod
     public void setVisibility(int v) {
         if (getVisibility() != v) {
             super.setVisibility(v);
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index 14ec8c6..ebbe1cd 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -34,6 +34,9 @@
  * a radio group, checking one radio button unchecks all the others.</p>
  * </p>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
  * <p><strong>XML attributes</strong></p>
  * <p> 
  * See {@link android.R.styleable#CompoundButton CompoundButton Attributes}, 
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 1800c5a..28499d0 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -41,6 +41,9 @@
  * <p>
  * The secondary progress should not be modified by the client as it is used
  * internally as the background for a fractionally filled star.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
  * 
  * @attr ref android.R.styleable#RatingBar_numStars
  * @attr ref android.R.styleable#RatingBar_rating
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 64cda49..a47359f 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -54,6 +54,9 @@
  * {@link #ALIGN_PARENT_BOTTOM}.
  * </p>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-relativelayout.html">Relative
+ * Layout tutorial</a>.</p>
+ *
  * <p>
  * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
  * layout attributes
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index f23a723..0ca71e1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.app.PendingIntent;
+import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -110,6 +111,42 @@
         }
     }
 
+    private class SetEmptyView extends Action {
+        int viewId;
+        int emptyViewId;
+
+        public final static int TAG = 6;
+
+        SetEmptyView(int viewId, int emptyViewId) {
+            this.viewId = viewId;
+            this.emptyViewId = emptyViewId;
+        }
+
+        SetEmptyView(Parcel in) {
+            this.viewId = in.readInt();
+            this.emptyViewId = in.readInt();
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeInt(TAG);
+            out.writeInt(this.viewId);
+            out.writeInt(this.emptyViewId);
+        }
+
+        @Override
+        public void apply(View root) {
+            final View view = root.findViewById(viewId);
+            if (!(view instanceof AdapterView<?>)) return;
+
+            AdapterView<?> adapterView = (AdapterView<?>) view;
+
+            final View emptyView = root.findViewById(emptyViewId);
+            if (emptyView == null) return;
+
+            adapterView.setEmptyView(emptyView);
+        }
+    }
+
     /**
      * Equivalent to calling
      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
@@ -631,6 +668,9 @@
                 case ReflectionActionWithoutParams.TAG:
                     mActions.add(new ReflectionActionWithoutParams(parcel));
                     break;
+                case SetEmptyView.TAG:
+                    mActions.add(new SetEmptyView(parcel));
+                    break;
                 default:
                     throw new ActionException("Tag " + tag + " not found");
                 }
@@ -760,6 +800,16 @@
     }
 
     /**
+     * Equivalent to calling AdapterView.setEmptyView
+     *
+     * @param viewId The id of the view on which to set the empty view
+     * @param emptyViewId The view id of the empty view
+     */
+    public void setEmptyView(int viewId, int emptyViewId) {
+        addAction(new SetEmptyView(viewId, emptyViewId));
+    }
+
+    /**
      * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
      * {@link Chronometer#setFormat Chronometer.setFormat},
      * and {@link Chronometer#start Chronometer.start()} or
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 60e8568..b534c34 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -33,6 +33,9 @@
  * A view that displays one child at a time and lets the user pick among them.
  * The items in the Spinner come from the {@link Adapter} associated with
  * this view.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Spinner
+ * tutorial</a>.</p>
  * 
  * @attr ref android.R.styleable#Spinner_prompt
  */
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index c3e8838..9816b39 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -27,6 +27,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -66,8 +67,6 @@
     private static final float SLIDE_UP_RATIO = 0.7f;
 
     private final WeakHashMap<View, Float> mRotations = new WeakHashMap<View, Float>();
-    private final WeakHashMap<View, Integer>
-            mChildrenToApplyTransformsTo = new WeakHashMap<View, Integer>();
 
     /**
      * Sentinel value for no current active pointer.
@@ -90,9 +89,12 @@
     private int mMaximumVelocity;
     private VelocityTracker mVelocityTracker;
 
+    private static HolographicHelper sHolographicHelper;
     private ImageView mHighlight;
     private StackSlider mStackSlider;
     private boolean mFirstLayoutHappened = false;
+    private ViewGroup mAncestorContainingAllChildren = null;
+    private int mAncestorHeight = 0;
 
     public StackView(Context context) {
         super(context);
@@ -117,9 +119,14 @@
         addViewInLayout(mHighlight, -1, new LayoutParams(mHighlight));
         mStackSlider = new StackSlider();
 
-        if (!sPaintsInitialized) {
-            initializePaints();
+        if (sHolographicHelper == null) {
+            sHolographicHelper = new HolographicHelper();
         }
+        setClipChildren(false);
+        setClipToPadding(false);
+
+        // This is a flag to indicate the the stack is loading for the first time
+        mWhichChild = -1;
     }
 
     /**
@@ -205,6 +212,7 @@
         if (!mRotations.containsKey(child)) {
             float rotation = (float) (Math.random()*26 - 13);
             mRotations.put(child, rotation);
+            child.setRotation(rotation);
         }
 
         // Child has been removed
@@ -212,47 +220,36 @@
             if (mRotations.containsKey(child)) {
                 mRotations.remove(child);
             }
-            if (mChildrenToApplyTransformsTo.containsKey(child)) {
-                mChildrenToApplyTransformsTo.remove(child);
-            }
         }
-
-        // if this view is already in the layout, we need to
-        // wait until layout has finished in order to set the
-        // pivot point of the rotation (requiring getMeasuredWidth/Height())
-        mChildrenToApplyTransformsTo.put(child, relativeIndex);
     }
 
     @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+    }
 
-        if (!mChildrenToApplyTransformsTo.isEmpty()) {
-            for (View child: mChildrenToApplyTransformsTo.keySet()) {
-                if (mRotations.containsKey(child)) {
-                    child.setPivotX(child.getMeasuredWidth()/2);
-                    child.setPivotY(child.getMeasuredHeight()/2);
-                    child.setRotation(mRotations.get(child));
-                }
+    // TODO: right now, this code walks up the hierarchy as far as needed and disables clipping
+    // so that the stack's children can draw outside of the stack's bounds. This is fine within
+    // the context of widgets in the launcher, but is destructive in general, as the clipping
+    // values are not being reset. For this to be a full framework level widget, we will need
+    // framework level support for drawing outside of a parent's bounds.
+    private void disableParentalClipping() {
+        if (mAncestorContainingAllChildren != null) {
+            Log.v(TAG, "Disabling parental clipping.");
+            ViewGroup vg = this;
+            while (vg.getParent() != null && vg.getParent() instanceof ViewGroup) {
+                if (vg == mAncestorContainingAllChildren) break;
+                vg = (ViewGroup) vg.getParent();
+                vg.setClipChildren(false);
+                vg.setClipToPadding(false);
             }
-            mChildrenToApplyTransformsTo.clear();
         }
+    }
 
+    private void onLayout() {
         if (!mFirstLayoutHappened) {
             mViewHeight = Math.round(SLIDE_UP_RATIO*getMeasuredHeight());
             mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight);
-
-            // TODO: Right now this walks all the way up the view hierarchy and disables
-            // ClipChildren and ClipToPadding. We're probably going  to want to reset
-            // these flags as well.
-            setClipChildren(false);
-            setClipToPadding(false);
-            ViewGroup view = this;
-            while (view.getParent() != null && view.getParent() instanceof ViewGroup) {
-                view = (ViewGroup) view.getParent();
-                view.setClipChildren(false);
-                view.setClipToPadding(false);
-            }
             mFirstLayoutHappened = true;
         }
     }
@@ -261,7 +258,6 @@
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         int action = ev.getAction();
         switch(action & MotionEvent.ACTION_MASK) {
-
             case MotionEvent.ACTION_DOWN: {
                 if (mActivePointerId == INVALID_POINTER) {
                     mInitialX = ev.getX();
@@ -320,7 +316,7 @@
             View v = getViewAtRelativeIndex(activeIndex);
             if (v == null) return;
 
-            mHighlight.setImageBitmap(createOutline(v));
+            mHighlight.setImageBitmap(sHolographicHelper.createOutline(v));
             mHighlight.bringToFront();
             v.bringToFront();
             mStackSlider.setView(v);
@@ -640,64 +636,218 @@
         float getXProgress() {
             return mXProgress;
         }
-
     }
 
     @Override
     public void onRemoteAdapterConnected() {
         super.onRemoteAdapterConnected();
+        // On first run, we want to set the stack to the end.
+        if (mAdapter != null && mWhichChild == -1) {
+            mWhichChild = mAdapter.getCount() - 1;
+        }
         setDisplayedChild(mWhichChild);
     }
 
-    private static final Paint sHolographicPaint = new Paint();
-    private static final Paint sErasePaint = new Paint();
-    private static boolean sPaintsInitialized = false;
-    private static final float STROKE_WIDTH = 3.0f;
-
-    static void initializePaints() {
-        sHolographicPaint.setColor(0xff6699ff);
-        sHolographicPaint.setFilterBitmap(true);
-        sErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
-        sErasePaint.setFilterBitmap(true);
-        sPaintsInitialized = true;
+    LayoutParams createOrReuseLayoutParams(View v) {
+        final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
+        if (currentLp instanceof LayoutParams) {
+            LayoutParams lp = (LayoutParams) currentLp;
+            lp.setHorizontalOffset(0);
+            lp.setVerticalOffset(0);
+            return lp;
+        }
+        return new LayoutParams(v);
     }
 
-    static Bitmap createOutline(View v) {
-        if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) {
-            return null;
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        boolean dataChanged = mDataChanged;
+        if (dataChanged) {
+            handleDataChanged();
+
+            // if the data changes, mWhichChild might be out of the bounds of the adapter
+            // in this case, we reset mWhichChild to the beginning
+            if (mWhichChild >= mAdapter.getCount())
+                mWhichChild = 0;
+
+            showOnly(mWhichChild, true, true);
         }
 
-        Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
-                Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
 
-        float rotationX = v.getRotationX();
-        v.setRotationX(0);
-        canvas.concat(v.getMatrix());
-        v.draw(canvas);
-        v.setRotationX(rotationX);
+            int childRight = mPaddingLeft + child.getMeasuredWidth();
+            int childBottom = mPaddingTop + child.getMeasuredHeight();
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
 
-        Bitmap outlineBitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
-                Bitmap.Config.ARGB_8888);
-        Canvas outlineCanvas = new Canvas(outlineBitmap);
-        drawOutline(outlineCanvas, bitmap);
-        bitmap.recycle();
-        return outlineBitmap;
+            child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
+                    childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
+
+            //TODO: temp until fix in View
+            child.setPivotX(child.getMeasuredWidth()/2);
+            child.setPivotY(child.getMeasuredHeight()/2);
+        }
+
+        mDataChanged = false;
+        onLayout();
     }
 
-    static void drawOutline(Canvas dest, Bitmap src) {
-        dest.drawColor(0, PorterDuff.Mode.CLEAR);
+    class LayoutParams extends ViewGroup.LayoutParams {
+        int horizontalOffset;
+        int verticalOffset;
+        View mView;
 
-        Bitmap mask = src.extractAlpha();
-        Matrix id = new Matrix();
+        LayoutParams(View view) {
+            super(0, 0);
+            horizontalOffset = 0;
+            verticalOffset = 0;
+            mView = view;
+        }
 
-        Matrix m = new Matrix();
-        float xScale = STROKE_WIDTH*2/(src.getWidth());
-        float yScale = STROKE_WIDTH*2/(src.getHeight());
-        m.preScale(1+xScale, 1+yScale, src.getWidth()/2, src.getHeight()/2);
-        dest.drawBitmap(mask, m, sHolographicPaint);
+        LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            horizontalOffset = 0;
+            verticalOffset = 0;
+        }
 
-        dest.drawBitmap(src, id, sErasePaint);
-        mask.recycle();
+        private Rect parentRect = new Rect();
+        void invalidateGlobalRegion(View v, Rect r) {
+            View p = v;
+            if (!(v.getParent() != null && v.getParent() instanceof View)) return;
+
+            View gp = (View) v.getParent();
+            boolean firstPass = true;
+            parentRect.set(0, 0, 0, 0);
+            int depth = 0;
+            while (gp.getParent() != null && gp.getParent() instanceof View
+                    && !parentRect.contains(r)) {
+                if (!firstPass) {
+                    r.offset(p.getLeft() - gp.getScrollX(), p.getTop() - gp.getScrollY());
+                    depth++;
+                }
+                firstPass = false;
+                p = (View) p.getParent();
+                gp = (View) p.getParent();
+                parentRect.set(p.getLeft() - gp.getScrollX(), p.getTop() - gp.getScrollY(),
+                        p.getRight() - gp.getScrollX(), p.getBottom() - gp.getScrollY());
+            }
+
+            if (depth > mAncestorHeight) {
+                mAncestorContainingAllChildren = (ViewGroup) p;
+                mAncestorHeight = depth;
+                disableParentalClipping();
+            }
+
+            p.invalidate(r.left, r.top, r.right, r.bottom);
+        }
+
+        private Rect invalidateRect = new Rect();
+        private RectF invalidateRectf = new RectF();
+        // This is public so that PropertyAnimator can access it
+        public void setVerticalOffset(int newVerticalOffset) {
+            int offsetDelta = newVerticalOffset - verticalOffset;
+            verticalOffset = newVerticalOffset;
+
+            if (mView != null) {
+                mView.requestLayout();
+                int top = Math.min(mView.getTop() + offsetDelta, mView.getTop());
+                int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom());
+
+                invalidateRectf.set(mView.getLeft(),  top, mView.getRight(), bottom);
+
+                float xoffset = -invalidateRectf.left;
+                float yoffset = -invalidateRectf.top;
+                invalidateRectf.offset(xoffset, yoffset);
+                mView.getMatrix().mapRect(invalidateRectf);
+                invalidateRectf.offset(-xoffset, -yoffset);
+                invalidateRect.set((int) Math.floor(invalidateRectf.left),
+                        (int) Math.floor(invalidateRectf.top),
+                        (int) Math.ceil(invalidateRectf.right),
+                        (int) Math.ceil(invalidateRectf.bottom));
+
+                invalidateGlobalRegion(mView, invalidateRect);
+            }
+        }
+
+        public void setHorizontalOffset(int newHorizontalOffset) {
+            int offsetDelta = newHorizontalOffset - horizontalOffset;
+            horizontalOffset = newHorizontalOffset;
+
+            if (mView != null) {
+                mView.requestLayout();
+                int left = Math.min(mView.getLeft() + offsetDelta, mView.getLeft());
+                int right = Math.max(mView.getRight() + offsetDelta, mView.getRight());
+                invalidateRectf.set(left,  mView.getTop(), right, mView.getBottom());
+
+                float xoffset = -invalidateRectf.left;
+                float yoffset = -invalidateRectf.top;
+                invalidateRectf.offset(xoffset, yoffset);
+                mView.getMatrix().mapRect(invalidateRectf);
+                invalidateRectf.offset(-xoffset, -yoffset);
+
+                invalidateRect.set((int) Math.floor(invalidateRectf.left),
+                        (int) Math.floor(invalidateRectf.top),
+                        (int) Math.ceil(invalidateRectf.right),
+                        (int) Math.ceil(invalidateRectf.bottom));
+
+                invalidateGlobalRegion(mView, invalidateRect);
+            }
+        }
+    }
+
+    private static class HolographicHelper {
+        private final Paint mHolographicPaint = new Paint();
+        private final Paint mErasePaint = new Paint();
+        private final float STROKE_WIDTH = 3.0f;
+
+        HolographicHelper() {
+            initializePaints();
+        }
+
+        void initializePaints() {
+            mHolographicPaint.setColor(0xff6699ff);
+            mHolographicPaint.setFilterBitmap(true);
+            mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+            mErasePaint.setFilterBitmap(true);
+        }
+
+        Bitmap createOutline(View v) {
+            if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) {
+                return null;
+            }
+
+            Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
+                    Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+
+            float rotationX = v.getRotationX();
+            v.setRotationX(0);
+            canvas.concat(v.getMatrix());
+            v.draw(canvas);
+
+            v.setRotationX(rotationX);
+
+            drawOutline(canvas, bitmap);
+            return bitmap;
+        }
+
+        final Matrix id = new Matrix();
+        final Matrix scaleMatrix = new Matrix();
+        void drawOutline(Canvas dest, Bitmap src) {
+            Bitmap mask = src.extractAlpha();
+
+            dest.drawColor(0, PorterDuff.Mode.CLEAR);
+
+            float xScale = STROKE_WIDTH*2/(dest.getWidth());
+            float yScale = STROKE_WIDTH*2/(dest.getHeight());
+
+            scaleMatrix.reset();
+            scaleMatrix.preScale(1+xScale, 1+yScale, dest.getWidth()/2, dest.getHeight()/2);
+            dest.setMatrix(id);
+            dest.drawBitmap(mask, scaleMatrix, mHolographicPaint);
+            dest.drawBitmap(mask, id, mErasePaint);
+            mask.recycle();
+        }
     }
 }
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 02cd6a8..f720cee 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -40,6 +40,9 @@
  * user clicks to select a specific tab, and a FrameLayout object that displays the contents of that
  * page. The individual elements are typically controlled using this container object, rather than
  * setting values on the child elements themselves.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Tab Layout
+ * tutorial</a>.</p>
  */
 public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchModeChangeListener {
 
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 4e1b585..afae7ef 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -40,6 +40,9 @@
  * handler, and manage callbacks. You might call this object to iterate the list
  * of tabs, or to tweak the layout of the tab list, but most methods should be
  * called on the containing TabHost object.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Tab Layout
+ * tutorial</a>.</p>
  * 
  * @attr ref android.R.styleable#TabWidget_divider
  * @attr ref android.R.styleable#TabWidget_tabStripEnabled
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index 73760ac..7f26e28 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -67,6 +67,9 @@
  * <p>Although the typical child of a TableLayout is a TableRow, you can
  * actually use any View subclass as a direct child of TableLayout. The View
  * will be displayed as a single row that spans all the table columns.</p>
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tablelayout.html">Table
+ * Layout tutorial</a>.</p>
  */
 public class TableLayout extends LinearLayout {
     private int[] mMaxWidths;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 25c5b24..961c782 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -22,7 +22,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.ClipboardManager;
-import android.content.ClippedData;
+import android.content.ClipData;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
@@ -6532,6 +6532,13 @@
             if (!mFrozenWithFocus || (selStart < 0 || selEnd < 0)) {
                 boolean selMoved = mSelectionMoved;
 
+                if (mSelectionModifierCursorController != null) {
+                    final int touchOffset = 
+                        ((SelectionModifierCursorController) mSelectionModifierCursorController).
+                        getMinTouchOffset();
+                    Selection.setSelection((Spannable) mText, touchOffset);
+                }
+
                 if (mMovement != null) {
                     mMovement.onTakeFocus(this, (Spannable) mText, direction);
                 }
@@ -6744,13 +6751,14 @@
             if (isTextEditable()) {
                 if (action == MotionEvent.ACTION_UP && isFocused() && !mScrolled) {
                     InputMethodManager imm = (InputMethodManager)
-                            getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+                          getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
 
                     final int newSelStart = getSelectionStart();
                     final int newSelEnd = getSelectionEnd();
 
                     CommitSelectionReceiver csr = null;
-                    if (newSelStart != oldSelStart || newSelEnd != oldSelEnd) {
+                    if (newSelStart != oldSelStart || newSelEnd != oldSelEnd ||
+                            didTouchFocusSelect()) {
                         csr = new CommitSelectionReceiver(oldSelStart, oldSelEnd,
                                 newSelStart, newSelEnd);
                     }
@@ -7105,14 +7113,14 @@
             return -1;
         }
 
-        int end = offset;
+        int len = mText.length();
+        int end = Math.min(offset, len);
 
         if (end < 0) {
             return -1;
         }
 
         int start = end;
-        int len = mText.length();
 
         for (; start > 0; start--) {
             char c = mTransformed.charAt(start - 1);
@@ -7166,6 +7174,38 @@
         return (((long) start) << 32) | end;
     }
 
+    private void selectCurrentWord() {
+        // In case selection mode is started after an orientation change or after a select all,
+        // use the current selection instead of creating one
+        if (hasSelection()) {
+            return;
+        }
+
+        int selectionStart, selectionEnd;
+
+        // selectionModifierCursorController is not null at that point
+        SelectionModifierCursorController selectionModifierCursorController =
+            ((SelectionModifierCursorController) mSelectionModifierCursorController);
+        int minOffset = selectionModifierCursorController.getMinTouchOffset();
+        int maxOffset = selectionModifierCursorController.getMaxTouchOffset();
+
+        long wordLimits = getWordLimitsAt(minOffset);
+        if (wordLimits >= 0) {
+            selectionStart = (int) (wordLimits >>> 32);
+        } else {
+            selectionStart = Math.max(minOffset - 5, 0);
+        }
+
+        wordLimits = getWordLimitsAt(maxOffset);
+        if (wordLimits >= 0) {
+            selectionEnd = (int) (wordLimits & 0x00000000FFFFFFFFL);
+        } else {
+            selectionEnd = Math.min(maxOffset + 5, mText.length());
+        }
+
+        Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+    }
+
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         if (!isShown()) {
@@ -7271,17 +7311,16 @@
      * this will be {@link android.R.id#copyUrl} or {@link android.R.id#selectTextMode}.
      */
     public boolean onTextContextMenuItem(int id) {
-        int selStart = getSelectionStart();
-        int selEnd = getSelectionEnd();
-
-        if (!isFocused()) {
-            selStart = 0;
-            selEnd = mText.length();
+        int min = 0;
+        int max = mText.length();
+        
+        if (isFocused()) {
+            int selStart = getSelectionStart();
+            int selEnd = getSelectionEnd();
+            min = Math.max(0, Math.min(selStart, selEnd));
+            max = Math.max(0, Math.max(selStart, selEnd));
         }
 
-        int min = Math.max(0, Math.min(selStart, selEnd));
-        int max = Math.max(0, Math.max(selStart, selEnd));
-
         ClipboardManager clipboard = (ClipboardManager)getContext()
                 .getSystemService(Context.CLIPBOARD_SERVICE);
 
@@ -7291,17 +7330,18 @@
 
                 URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
                 if (urls.length >= 1) {
-                    ClippedData clip = null;
+                    ClipData clip = null;
                     for (int i=0; i<urls.length; i++) {
                         Uri uri = Uri.parse(urls[0].getURL());
-                        ClippedData.Item item = new ClippedData.Item(uri);
                         if (clip == null) {
-                            clip = new ClippedData(null, null, item);
+                            clip = ClipData.newRawUri(null, null, uri);
                         } else {
-                            clip.addItem(item);
+                            clip.addItem(new ClipData.Item(uri));
                         }
                     }
-                    clipboard.setPrimaryClip(clip);
+                    if (clip != null) {
+                        clipboard.setPrimaryClip(clip);
+                    }
                 }
                 return true;
 
@@ -7463,37 +7503,6 @@
             }
         }
 
-        private void selectCurrentWord() {
-            // In case selection mode is started after an orientation change, use the current
-            // selection instead of creating one
-            if (hasSelection()) {
-                return;
-            }
-
-            int selectionStart, selectionEnd;
-
-            SelectionModifierCursorController selectionModifierCursorController =
-                ((SelectionModifierCursorController) mSelectionModifierCursorController);
-            int minOffset = selectionModifierCursorController.getMinTouchOffset();
-            int maxOffset = selectionModifierCursorController.getMaxTouchOffset();
-
-            long wordLimits = getWordLimitsAt(minOffset);
-            if (wordLimits >= 0) {
-                selectionStart = (int) (wordLimits >>> 32);
-            } else {
-                selectionStart = Math.max(minOffset - 5, 0);
-            }
-
-            wordLimits = getWordLimitsAt(maxOffset);
-            if (wordLimits >= 0) {
-                selectionEnd = (int) (wordLimits & 0x00000000FFFFFFFFL);
-            } else {
-                selectionEnd = Math.min(maxOffset + 5, mText.length());
-            }
-
-            Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
-        }
-
         @Override
         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
             return true;
@@ -7528,7 +7537,7 @@
 
             switch (item.getItemId()) {
                 case ID_PASTE:
-                    ClippedData clip = clipboard.getPrimaryClip();
+                    ClipData clip = clipboard.getPrimaryClip();
                     if (clip != null) {
                         boolean didfirst = false;
                         for (int i=0; i<clip.getItemCount(); i++) {
@@ -7549,15 +7558,15 @@
                     return true;
 
                 case ID_CUT:
-                    clipboard.setPrimaryClip(new ClippedData(null, null,
-                            new ClippedData.Item(mTransformed.subSequence(min, max))));
+                    clipboard.setPrimaryClip(ClipData.newPlainText(null, null,
+                            mTransformed.subSequence(min, max)));
                     ((Editable) mText).delete(min, max);
                     stopSelectionActionMode();
                     return true;
 
                 case ID_COPY:
-                    clipboard.setPrimaryClip(new ClippedData(null, null,
-                            new ClippedData.Item(mTransformed.subSequence(min, max))));
+                    clipboard.setPrimaryClip(ClipData.newPlainText(null, null,
+                            mTransformed.subSequence(min, max)));
                     stopSelectionActionMode();
                     return true;
             }
@@ -7600,7 +7609,7 @@
         /**
          * Update the controller's position.
          */
-        public void updatePosition(int offset);
+        public void updatePosition(int x, int y);
 
         /**
          * The controller and the cursor's positions can be link by a fixed offset,
@@ -7635,8 +7644,8 @@
         // Vertical extension of the touch region
         int mTopExtension, mBottomExtension;
         // Position of the virtual finger position on screen
-        int mHopSpotVertcalPosition;
-        
+        int mHotSpotVerticalPosition;
+
         Handle(Drawable drawable) {
             mDrawable = drawable;
         }
@@ -7648,8 +7657,7 @@
             final int lineTop = mLayout.getLineTop(line);
             final int lineBottom = mLayout.getLineBottom(line);
 
-            mHopSpotVertcalPosition = lineTop + (bottom ? (3 * (lineBottom - lineTop)) / 4 :
-                (lineBottom - lineTop) / 4);
+            mHotSpotVerticalPosition = lineTop;
 
             final Rect bounds = sCursorControllerTempRect;
             bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0);
@@ -7669,7 +7677,7 @@
 
             int boundTopBefore = bounds.top;
             convertFromViewportToContentCoordinates(bounds);
-            mHopSpotVertcalPosition += bounds.top - boundTopBefore;
+            mHotSpotVerticalPosition += bounds.top - boundTopBefore;
             mDrawable.setBounds(bounds);
             postInvalidate();
         }
@@ -7720,7 +7728,7 @@
 
         public void show() {
             updateDrawablePosition();
-            // Has to be done after updatePosition, so that previous position invalidate
+            // Has to be done after updateDrawablePosition, so that previous position invalidate
             // in only done if necessary.
             mIsVisible = true;
         }
@@ -7757,12 +7765,14 @@
             }
         }
 
-        public void updatePosition(int offset) {
-            if (offset == getSelectionStart()) {
-                return; // No change, no need to redraw
+        public void updatePosition(int x, int y) {
+            final int previousOffset = getSelectionStart();
+            int offset = getHysteresisOffset(x, y, previousOffset);
+
+            if (offset != previousOffset) {
+                Selection.setSelection((Spannable) mText, offset);
+                updateDrawablePosition();
             }
-            Selection.setSelection((Spannable) mText, offset);
-            updateDrawablePosition();
         }
 
         private void updateDrawablePosition() {
@@ -7808,7 +7818,7 @@
 
                             final Rect bounds = mHandle.mDrawable.getBounds();
                             mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
-                            mOffsetY = mHandle.mHopSpotVertcalPosition - y;
+                            mOffsetY = mHandle.mHotSpotVerticalPosition - y;
 
                             mOnDownTimerStart = event.getEventTime();
                         }
@@ -7863,7 +7873,7 @@
 
         public void show() {
             updateDrawablesPositions();
-            // Has to be done after updatePosition, so that previous position invalidate
+            // Has to be done after updateDrawablePositions, so that previous position invalidate
             // in only done if necessary.
             mIsVisible = true;
             mFadeOutTimerStart = -1;
@@ -7905,10 +7915,13 @@
             }
         }
 
-        public void updatePosition(int offset) {
+        public void updatePosition(int x, int y) {
             int selectionStart = getSelectionStart();
             int selectionEnd = getSelectionEnd();
 
+            final int previousOffset = mStartIsDragged ? selectionStart : selectionEnd;
+            int offset = getHysteresisOffset(x, y, previousOffset);
+
             // Handle the case where start and end are swapped, making sure start <= end
             if (mStartIsDragged) {
                 if (offset <= selectionEnd) {
@@ -7955,7 +7968,8 @@
                 return;
             }
 
-            boolean oneLineSelection = mLayout.getLineForOffset(selectionStart) == mLayout.getLineForOffset(selectionEnd); 
+            boolean oneLineSelection =
+                mLayout.getLineForOffset(selectionStart) == mLayout.getLineForOffset(selectionEnd);
             mStartHandle.positionAtCursor(selectionStart, oneLineSelection);
             mEndHandle.positionAtCursor(selectionEnd, true);
 
@@ -7990,7 +8004,7 @@
                                     final Handle draggedHandle = mStartIsDragged ? mStartHandle : mEndHandle;
                                     final Rect bounds = draggedHandle.mDrawable.getBounds();
                                     mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
-                                    mOffsetY = draggedHandle.mHopSpotVertcalPosition - y;
+                                    mOffsetY = draggedHandle.mHotSpotVerticalPosition - y;
 
                                     ((ArrowKeyMovementMethod)mMovement).setCursorController(this);
                                 }
@@ -8060,6 +8074,15 @@
         stopSelectionActionMode();
     }
 
+    private int getOffsetForHorizontal(int line, int x) {
+        x -= getTotalPaddingLeft();
+        // Clamp the position to inside of the view.
+        x = Math.max(0, x);
+        x = Math.min(getWidth() - getTotalPaddingRight() - 1, x);
+        x += getScrollX();
+        return getLayout().getOffsetForHorizontal(line, x);
+    }
+
     /**
      * Get the offset character closest to the specified absolute position.
      *
@@ -8071,32 +8094,44 @@
      * @hide
      */
     public int getOffset(int x, int y) {
-        x -= getTotalPaddingLeft();
+        if (getLayout() == null) return -1;
+
         y -= getTotalPaddingTop();
-
         // Clamp the position to inside of the view.
-        if (x < 0) {
-            x = 0;
-        } else if (x >= (getWidth() - getTotalPaddingRight())) {
-            x = getWidth()-getTotalPaddingRight() - 1;
-        }
-        if (y < 0) {
-            y = 0;
-        } else if (y >= (getHeight() - getTotalPaddingBottom())) {
-            y = getHeight()-getTotalPaddingBottom() - 1;
-        }
-
-        x += getScrollX();
+        y = Math.max(0, y);
+        y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y);
         y += getScrollY();
 
-        Layout layout = getLayout();
-        if (layout != null) {
-            final int line = layout.getLineForVertical(y);
-            final int offset = layout.getOffsetForHorizontal(line, x);
-            return offset;
-        } else {
-            return -1;
+        final int line = getLayout().getLineForVertical(y);
+        final int offset = getOffsetForHorizontal(line, x);
+        return offset;
+    }
+
+    int getHysteresisOffset(int x, int y, int previousOffset) {
+        final Layout layout = getLayout();
+        if (layout == null) return -1;
+
+        y -= getTotalPaddingTop();
+        // Clamp the position to inside of the view.
+        y = Math.max(0, y);
+        y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y);
+        y += getScrollY();
+
+        int line = getLayout().getLineForVertical(y);
+
+        final int previousLine = layout.getLineForOffset(previousOffset);
+        final int previousLineTop = layout.getLineTop(previousLine);
+        final int previousLineBottom = layout.getLineBottom(previousLine);
+        final int hysteresisThreshold = (previousLineBottom - previousLineTop) / 2;
+
+        // If new line is just before or after previous line and y position is less than
+        // hysteresisThreshold away from previous line, keep cursor on previous line.
+        if (((line == previousLine + 1) && ((y - previousLineBottom) < hysteresisThreshold)) ||
+            ((line == previousLine - 1) && ((previousLineTop - y)    < hysteresisThreshold))) {
+            line = previousLine;
         }
+
+        return getOffsetForHorizontal(line, x);
     }
 
 
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index caed308..e61fac3 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -45,6 +45,9 @@
  * Under AM/PM mode, the user can hit 'a', 'A", 'p' or 'P' to pick.
  *
  * For a dialog using this view, see {@link android.app.TimePickerDialog}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker
+ * tutorial</a>.</p>
  */
 @Widget
 public class TimePicker extends FrameLayout {
diff --git a/core/java/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java
index dc791e3..3b680e8 100644
--- a/core/java/android/widget/ToggleButton.java
+++ b/core/java/android/widget/ToggleButton.java
@@ -26,6 +26,9 @@
 /**
  * Displays checked/unchecked states as a button
  * with a "light" indicator and by default accompanied with the text "ON" or "OFF".
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
  * 
  * @attr ref android.R.styleable#ToggleButton_textOn
  * @attr ref android.R.styleable#ToggleButton_textOff
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index ef0ab41..1048352 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -110,13 +110,13 @@
                 com.android.internal.R.id.lower_action_context_bar);
         mAnimatorView = (ViewAnimator) decor.findViewById(
                 com.android.internal.R.id.action_bar_animator);
-        mActionView.setContextView(mUpperContextView);
-        
+
         if (mActionView == null || mUpperContextView == null || mAnimatorView == null) {
             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
                     "with a compatible window decor layout");
         }
 
+        mActionView.setContextView(mUpperContextView);
         mContextDisplayMode = mLowerContextView == null ?
                 CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
     }
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index d1aff2a..e07c54f 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -134,7 +134,7 @@
     private static void beginShutdownSequence(Context context) {
         synchronized (sIsStartedGuard) {
             if (sIsStarted) {
-                Log.d(TAG, "Request to shutdown already running, returning.");
+                Log.d(TAG, "Shutdown sequence already running, returning.");
                 return;
             }
             sIsStarted = true;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a70dbf6..0a1c8ff 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3693,8 +3693,8 @@
                 mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
                 mDischargeCurrentLevel = level;
                 if (level < mDischargeUnplugLevel) {
-                    mLowDischargeAmountSinceCharge = mDischargeUnplugLevel-level-1;
-                    mHighDischargeAmountSinceCharge = mDischargeUnplugLevel-level;
+                    mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
+                    mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
                 }
                 doPlugLocked(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
             }
diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java
index 7138b5c..d38c03b 100644
--- a/core/java/com/android/internal/util/HierarchicalStateMachine.java
+++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java
@@ -137,7 +137,7 @@
     }
 
     class State1 extends HierarchicalState {
-        @Override public boolean processMessage(Message message) {
+        \@Override public boolean processMessage(Message message) {
             Log.d(TAG, "Hello World");
             return HANDLED;
         }
@@ -257,10 +257,10 @@
     }
 
     class P1 extends HierarchicalState {
-        @Override public void enter() {
+        \@Override public void enter() {
             Log.d(TAG, "mP1.enter");
         }
-        @Override public boolean processMessage(Message message) {
+        \@Override public boolean processMessage(Message message) {
             boolean retVal;
             Log.d(TAG, "mP1.processMessage what=" + message.what);
             switch(message.what) {
@@ -278,16 +278,16 @@
             }
             return retVal;
         }
-        @Override public void exit() {
+        \@Override public void exit() {
             Log.d(TAG, "mP1.exit");
         }
     }
 
     class S1 extends HierarchicalState {
-        @Override public void enter() {
+        \@Override public void enter() {
             Log.d(TAG, "mS1.enter");
         }
-        @Override public boolean processMessage(Message message) {
+        \@Override public boolean processMessage(Message message) {
             Log.d(TAG, "S1.processMessage what=" + message.what);
             if (message.what == CMD_1) {
                 // Transition to ourself to show that enter/exit is called
@@ -298,16 +298,16 @@
                 return NOT_HANDLED;
             }
         }
-        @Override public void exit() {
+        \@Override public void exit() {
             Log.d(TAG, "mS1.exit");
         }
     }
 
     class S2 extends HierarchicalState {
-        @Override public void enter() {
+        \@Override public void enter() {
             Log.d(TAG, "mS2.enter");
         }
-        @Override public boolean processMessage(Message message) {
+        \@Override public boolean processMessage(Message message) {
             boolean retVal;
             Log.d(TAG, "mS2.processMessage what=" + message.what);
             switch(message.what) {
@@ -326,17 +326,17 @@
             }
             return retVal;
         }
-        @Override public void exit() {
+        \@Override public void exit() {
             Log.d(TAG, "mS2.exit");
         }
     }
 
     class P2 extends HierarchicalState {
-        @Override public void enter() {
+        \@Override public void enter() {
             Log.d(TAG, "mP2.enter");
             sendMessage(obtainMessage(CMD_5));
         }
-        @Override public boolean processMessage(Message message) {
+        \@Override public boolean processMessage(Message message) {
             Log.d(TAG, "P2.processMessage what=" + message.what);
             switch(message.what) {
             case(CMD_3):
@@ -349,12 +349,12 @@
             }
             return HANDLED;
         }
-        @Override public void exit() {
+        \@Override public void exit() {
             Log.d(TAG, "mP2.exit");
         }
     }
 
-    @Override
+    \@Override
     void halting() {
         Log.d(TAG, "halting");
         synchronized (this) {
diff --git a/core/java/com/android/internal/util/TypedProperties.java b/core/java/com/android/internal/util/TypedProperties.java
index c2ce210..5613999 100644
--- a/core/java/com/android/internal/util/TypedProperties.java
+++ b/core/java/com/android/internal/util/TypedProperties.java
@@ -412,7 +412,7 @@
      */
 
     /**
-     * An unchecked exception that is thrown if a {@code get&lt;TYPE&gt;()} method
+     * An unchecked exception that is thrown if a {@code get<TYPE>()} method
      * is used to retrieve a parameter whose type does not match the method name.
      */
     public static class TypeException extends IllegalArgumentException {
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index a765e38..986ba38 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -31,9 +31,10 @@
 
 public class IInputConnectionWrapper extends IInputContext.Stub {
     static final String TAG = "IInputConnectionWrapper";
-    
+
     private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
     private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
+    private static final int DO_GET_SELECTED_TEXT = 25;
     private static final int DO_GET_CURSOR_CAPS_MODE = 30;
     private static final int DO_GET_EXTRACTED_TEXT = 40;
     private static final int DO_COMMIT_TEXT = 50;
@@ -42,6 +43,7 @@
     private static final int DO_PERFORM_EDITOR_ACTION = 58;
     private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
     private static final int DO_SET_COMPOSING_TEXT = 60;
+    private static final int DO_SET_COMPOSING_REGION = 63;
     private static final int DO_FINISH_COMPOSING_TEXT = 65;
     private static final int DO_SEND_KEY_EVENT = 70;
     private static final int DO_DELETE_SURROUNDING_TEXT = 80;
@@ -50,7 +52,7 @@
     private static final int DO_REPORT_FULLSCREEN_MODE = 100;
     private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
     private static final int DO_CLEAR_META_KEY_STATES = 130;
-        
+
     private WeakReference<InputConnection> mInputConnection;
 
     private Looper mMainLooper;
@@ -92,6 +94,10 @@
         dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback));
     }
 
+    public void getSelectedText(int flags, int seq, IInputContextCallback callback) {
+        dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback));
+    }
+
     public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) {
         dispatchMessage(obtainMessageISC(DO_GET_CURSOR_CAPS_MODE, reqModes, seq, callback));
     }
@@ -122,6 +128,10 @@
         dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0));
     }
     
+    public void setComposingRegion(int start, int end) {
+        dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end));
+    }
+
     public void setComposingText(CharSequence text, int newCursorPosition) {
         dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
     }
@@ -206,6 +216,22 @@
                 }
                 return;
             }
+            case DO_GET_SELECTED_TEXT: {
+                SomeArgs args = (SomeArgs)msg.obj;
+                try {
+                    InputConnection ic = mInputConnection.get();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "getSelectedText on inactive InputConnection");
+                        args.callback.setSelectedText(null, args.seq);
+                        return;
+                    }
+                    args.callback.setSelectedText(ic.getSelectedText(
+                            msg.arg1), args.seq);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Got RemoteException calling setSelectedText", e);
+                }
+                return;
+            }
             case DO_GET_CURSOR_CAPS_MODE: {
                 SomeArgs args = (SomeArgs)msg.obj;
                 try {
@@ -292,6 +318,15 @@
                 ic.setComposingText((CharSequence)msg.obj, msg.arg1);
                 return;
             }
+            case DO_SET_COMPOSING_REGION: {
+                InputConnection ic = mInputConnection.get();
+                if (ic == null || !isActive()) {
+                    Log.w(TAG, "setComposingRegion on inactive InputConnection");
+                    return;
+                }
+                ic.setComposingRegion(msg.arg1, msg.arg2);
+                return;
+            }
             case DO_FINISH_COMPOSING_TEXT: {
                 InputConnection ic = mInputConnection.get();
                 // Note we do NOT check isActive() here, because this is safe
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 02cb9e4..333fc82 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -65,4 +65,8 @@
     void clearMetaKeyStates(int states);
     
     void performPrivateCommand(String action, in Bundle data);
+
+    void setComposingRegion(int start, int end);
+
+    void getSelectedText(int flags, int seq, IInputContextCallback callback);
 }
diff --git a/core/java/com/android/internal/view/IInputContextCallback.aidl b/core/java/com/android/internal/view/IInputContextCallback.aidl
index 9b8c43c..661066b 100644
--- a/core/java/com/android/internal/view/IInputContextCallback.aidl
+++ b/core/java/com/android/internal/view/IInputContextCallback.aidl
@@ -26,4 +26,5 @@
     void setTextAfterCursor(CharSequence textAfterCursor, int seq);
     void setCursorCapsMode(int capsMode, int seq);
     void setExtractedText(in ExtractedText extractedText, int seq);
+    void setSelectedText(CharSequence selectedText, int seq);
 }
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 3c44e58..08c3026 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.view;
 
-import com.android.internal.view.IInputContext;
-
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -38,6 +36,7 @@
         public boolean mHaveValue;
         public CharSequence mTextBeforeCursor;
         public CharSequence mTextAfterCursor;
+        public CharSequence mSelectedText;
         public ExtractedText mExtractedText;
         public int mCursorCapsMode;
         
@@ -114,6 +113,19 @@
             }
         }
 
+        public void setSelectedText(CharSequence selectedText, int seq) {
+            synchronized (this) {
+                if (seq == mSeq) {
+                    mSelectedText = selectedText;
+                    mHaveValue = true;
+                    notifyAll();
+                } else {
+                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
+                            + ") in setSelectedText, ignoring.");
+                }
+            }
+        }
+
         public void setCursorCapsMode(int capsMode, int seq) {
             synchronized (this) {
                 if (seq == mSeq) {
@@ -203,6 +215,24 @@
         return value;
     }
     
+    public CharSequence getSelectedText(int flags) {
+        CharSequence value = null;
+        try {
+            InputContextCallback callback = InputContextCallback.getInstance();
+            mIInputContext.getSelectedText(flags, callback.mSeq, callback);
+            synchronized (callback) {
+                callback.waitForResultLocked();
+                if (callback.mHaveValue) {
+                    value = callback.mSelectedText;
+                }
+            }
+            callback.dispose();
+        } catch (RemoteException e) {
+            return null;
+        }
+        return value;
+    }
+
     public int getCursorCapsMode(int reqModes) {
         int value = 0;
         try {
@@ -283,7 +313,16 @@
             return false;
         }
     }
-    
+
+    public boolean setComposingRegion(int start, int end) {
+        try {
+            mIInputContext.setComposingRegion(start, end);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     public boolean setComposingText(CharSequence text, int newCursorPosition) {
         try {
             mIInputContext.setComposingText(text, newCursorPosition);
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index cb5f179..e064e2c 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -25,7 +25,6 @@
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
 /**
@@ -41,7 +40,13 @@
     private int mMaxItems;
     private boolean mReserveOverflow;
     private OverflowMenuButton mOverflowButton;
-    private WeakReference<MenuPopupHelper> mOverflowPopup;
+    private MenuPopupHelper mOverflowPopup;
+
+    private Runnable mShowOverflow = new Runnable() {
+        public void run() {
+            showOverflowMenu();
+        }
+    };
     
     public ActionMenuView(Context context) {
         this(context, null);
@@ -56,18 +61,40 @@
                 com.android.internal.R.styleable.Theme_actionButtonPadding, 0);
         mItemMargin = mItemPadding / 2;
         a.recycle();
-        
+
+        // Measure for initial configuration
+        mMaxItems = measureMaxActionButtons();
+
+        // TODO There has to be a better way to indicate that we don't have a hard menu key.
+        final int screen = getResources().getConfiguration().screenLayout;
+        mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
+                Configuration.SCREENLAYOUT_SIZE_XLARGE;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        final int screen = newConfig.screenLayout;
+        mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
+                Configuration.SCREENLAYOUT_SIZE_XLARGE;
+        mMaxItems = measureMaxActionButtons();
+        if (mMenu != null) {
+            mMenu.setMaxActionItems(mMaxItems);
+            updateChildren(false);
+        }
+
+        if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
+            mOverflowPopup.dismiss();
+            post(mShowOverflow);
+        }
+    }
+
+    private int measureMaxActionButtons() {
         final Resources res = getResources();
         final int size = res.getDimensionPixelSize(com.android.internal.R.dimen.action_icon_size);
         final int spaceAvailable = res.getDisplayMetrics().widthPixels / 2;
         final int itemSpace = size + mItemPadding;
         
-        mMaxItems = spaceAvailable / (itemSpace > 0 ? itemSpace : 1);
-
-        // TODO There has to be a better way to indicate that we don't have a hard menu key.
-        final int screen = res.getConfiguration().screenLayout;
-        mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
-                Configuration.SCREENLAYOUT_SIZE_XLARGE;
+        return spaceAvailable / (itemSpace > 0 ? itemSpace : 1);
     }
 
     public boolean isOverflowReserved() {
@@ -156,14 +183,14 @@
                     popup.show();
                 }
             });
-            mOverflowPopup = new WeakReference<MenuPopupHelper>(popup);
+            mOverflowPopup = popup;
             return true;
         }
         return false;
     }
 
     public boolean isOverflowMenuShowing() {
-        MenuPopupHelper popup = mOverflowPopup != null ? mOverflowPopup.get() : null;
+        MenuPopupHelper popup = mOverflowPopup;
         if (popup != null) {
             return popup.isShowing();
         }
@@ -171,7 +198,7 @@
     }
 
     public boolean hideOverflowMenu() {
-        MenuPopupHelper popup = mOverflowPopup != null ? mOverflowPopup.get() : null;
+        MenuPopupHelper popup = mOverflowPopup;
         if (popup != null) {
             popup.dismiss();
             return true;
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index f52c93c..a12a4d6 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -26,6 +26,7 @@
 import android.view.View.MeasureSpec;
 import android.widget.AdapterView;
 import android.widget.ListPopupWindow;
+import android.widget.PopupWindow;
 
 import java.lang.ref.WeakReference;
 
@@ -42,6 +43,12 @@
     private WeakReference<View> mAnchorView;
     private boolean mOverflowOnly;
 
+    private PopupWindow.OnDismissListener mDismissListener = new PopupWindow.OnDismissListener() {
+        public void onDismiss() {
+            mPopup = null;
+        }
+    };
+
     public MenuPopupHelper(Context context, MenuBuilder menu) {
         this(context, menu, null, false);
     }
@@ -69,6 +76,7 @@
         mPopup = new ListPopupWindow(mContext, null, 0,
                 com.android.internal.R.style.Widget_Spinner);
         mPopup.setOnItemClickListener(this);
+        mPopup.setOnDismissListener(mDismissListener);
 
         final MenuAdapter adapter = mOverflowOnly ?
                 mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) :
@@ -95,7 +103,6 @@
         if (isShowing()) {
             mPopup.dismiss();
         }
-        mPopup = null;
     }
 
     public boolean isShowing() {
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 067d022..b9e4e46 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -221,6 +221,14 @@
         return false;
     }
 
+    public void postShowOverflowMenu() {
+        post(new Runnable() {
+            public void run() {
+                showOverflowMenu();
+            }
+        });
+    }
+
     public boolean hideOverflowMenu() {
         if (mMenuView != null) {
             return mMenuView.hideOverflowMenu();
diff --git a/core/java/com/android/internal/widget/CarouselView.java b/core/java/com/android/internal/widget/CarouselView.java
index e0c65dc..217805b 100644
--- a/core/java/com/android/internal/widget/CarouselView.java
+++ b/core/java/com/android/internal/widget/CarouselView.java
@@ -16,21 +16,26 @@
 
 package com.android.internal.widget;
 
+import com.android.internal.R;
 import com.android.internal.widget.CarouselRS.CarouselCallback;
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.graphics.Bitmap.Config;
 import android.renderscript.FileA3D;
 import android.renderscript.Mesh;
 import android.renderscript.RSSurfaceView;
 import android.renderscript.RenderScriptGL;
+import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
 
 public class CarouselView extends RSSurfaceView {
+    private static final boolean USE_DEPTH_BUFFER = true;
     private final int DEFAULT_SLOT_COUNT = 10;
     private final Bitmap DEFAULT_BITMAP = Bitmap.createBitmap(1, 1, Config.RGB_565);
     private static final String TAG = "CarouselView";
@@ -46,20 +51,28 @@
     private int mVisibleSlots = 0;
     private float mStartAngle;
     private int mSlotCount = DEFAULT_SLOT_COUNT;
-    
+
     public CarouselView(Context context) {
-        super(context);
+        this(context, null);
+    }
+
+    /**
+     * Constructor used when this widget is created from a layout file.
+     */
+    public CarouselView(Context context, AttributeSet attrs) {
+        super(context, attrs);
         mContext = context;
         boolean useDepthBuffer = true;
-        mRS = createRenderScript(useDepthBuffer);
+        mRS = createRenderScript(USE_DEPTH_BUFFER);
         mRenderScript = new CarouselRS();
         mRenderScript.init(mRS, getResources());
+        // TODO: add parameters to layout
     }
-    
+
     @Override
     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
         super.surfaceChanged(holder, format, w, h);
-        mRS.contextSetSurface(w, h, holder.getSurface());
+        //mRS.contextSetSurface(w, h, holder.getSurface());
         mRenderScript.init(mRS, getResources());
         setSlotCount(mSlotCount);
         createCards(mCardCount);
@@ -172,11 +185,20 @@
     
     @Override
     protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
         if(mRS != null) {
             mRS = null;
             destroyRenderScript();
         }
     }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mRS == null) {
+            mRS = createRenderScript(USE_DEPTH_BUFFER);
+        }
+    }
     
     @Override
     public boolean onTouchEvent(MotionEvent event) {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 0b62a67..90423be 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -19,6 +19,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.os.FileObserver;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -33,6 +34,7 @@
 import com.android.internal.telephony.ITelephony;
 import com.google.android.collect.Lists;
 
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -40,6 +42,7 @@
 import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Utilities for the lock patten and its settings.
@@ -48,8 +51,9 @@
 
     private static final String TAG = "LockPatternUtils";
 
-    private static final String LOCK_PATTERN_FILE = "/system/gesture.key";
-    private static final String LOCK_PASSWORD_FILE = "/system/password.key";
+    private static final String SYSTEM_DIRECTORY = "/system/";
+    private static final String LOCK_PATTERN_FILE = "gesture.key";
+    private static final String LOCK_PASSWORD_FILE = "password.key";
 
     /**
      * The maximum number of incorrect attempts before the user is prevented
@@ -100,6 +104,10 @@
     private static String sLockPatternFilename;
     private static String sLockPasswordFilename;
 
+    private static final AtomicBoolean sHaveNonZeroPatternFile = new AtomicBoolean(false);
+    private static final AtomicBoolean sHaveNonZeroPasswordFile = new AtomicBoolean(false);
+    private static FileObserver sPasswordObserver;
+
     public DevicePolicyManager getDevicePolicyManager() {
         if (mDevicePolicyManager == null) {
             mDevicePolicyManager =
@@ -117,14 +125,31 @@
     public LockPatternUtils(Context context) {
         mContext = context;
         mContentResolver = context.getContentResolver();
-        // Initialize the location of gesture lock file
-        if (sLockPatternFilename == null) {
-            sLockPatternFilename = android.os.Environment.getDataDirectory()
-                    .getAbsolutePath() + LOCK_PATTERN_FILE;
-            sLockPasswordFilename = android.os.Environment.getDataDirectory()
-                    .getAbsolutePath() + LOCK_PASSWORD_FILE;
-        }
 
+        // Initialize the location of gesture & PIN lock files
+        if (sLockPatternFilename == null) {
+            String dataSystemDirectory =
+                    android.os.Environment.getDataDirectory().getAbsolutePath() +
+                    SYSTEM_DIRECTORY;
+            sLockPatternFilename =  dataSystemDirectory + LOCK_PATTERN_FILE;
+            sLockPasswordFilename = dataSystemDirectory + LOCK_PASSWORD_FILE;
+            sHaveNonZeroPatternFile.set(new File(sLockPatternFilename).length() > 0);
+            sHaveNonZeroPasswordFile.set(new File(sLockPasswordFilename).length() > 0);
+            int fileObserverMask = FileObserver.CLOSE_WRITE | FileObserver.DELETE |
+                    FileObserver.MOVED_TO | FileObserver.CREATE;
+            sPasswordObserver = new FileObserver(dataSystemDirectory, fileObserverMask) {
+                    public void onEvent(int event, String path) {
+                        if (LOCK_PATTERN_FILE.equals(path)) {
+                            Log.d(TAG, "lock pattern file changed");
+                            sHaveNonZeroPatternFile.set(new File(sLockPatternFilename).length() > 0);
+                        } else if (LOCK_PASSWORD_FILE.equals(path)) {
+                            Log.d(TAG, "lock password file changed");
+                            sHaveNonZeroPasswordFile.set(new File(sLockPasswordFilename).length() > 0);
+                        }
+                    }
+                };
+            sPasswordObserver.startWatching();
+        }
     }
 
     public int getRequestedMinimumPasswordLength() {
@@ -258,32 +283,11 @@
     }
 
     /**
-     * Checks to see if the given file exists and contains any data. Returns
-     * true if it does, false otherwise.
-     *
-     * @param filename
-     * @return true if file exists and is non-empty.
-     */
-    private boolean nonEmptyFileExists(String filename) {
-        try {
-            // Check if we can read a byte from the file
-            RandomAccessFile raf = new RandomAccessFile(filename, "r");
-            raf.readByte();
-            raf.close();
-            return true;
-        } catch (FileNotFoundException fnfe) {
-            return false;
-        } catch (IOException ioe) {
-            return false;
-        }
-    }
-
-    /**
      * Check to see if the user has stored a lock pattern.
      * @return Whether a saved pattern exists.
      */
     public boolean savedPatternExists() {
-        return nonEmptyFileExists(sLockPatternFilename);
+        return sHaveNonZeroPatternFile.get();
     }
 
     /**
@@ -291,7 +295,7 @@
      * @return Whether a saved pattern exists.
      */
     public boolean savedPasswordExists() {
-        return nonEmptyFileExists(sLockPasswordFilename);
+        return sHaveNonZeroPasswordFile.get();
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/carousel.rs b/core/java/com/android/internal/widget/carousel.rs
index 4cfcbf4..87e24c0 100644
--- a/core/java/com/android/internal/widget/carousel.rs
+++ b/core/java/com/android/internal/widget/carousel.rs
@@ -53,19 +53,22 @@
 };
 
 // Client messages *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. ***
-const int CMD_CARD_SELECTED = 100;
-const int CMD_REQUEST_TEXTURE = 200;
-const int CMD_INVALIDATE_TEXTURE = 210;
-const int CMD_REQUEST_GEOMETRY = 300;
-const int CMD_INVALIDATE_GEOMETRY = 310;
-const int CMD_ANIMATION_STARTED = 400;
-const int CMD_ANIMATION_FINISHED = 500;
-const int CMD_PING = 600;
+static const int CMD_CARD_SELECTED = 100;
+static const int CMD_REQUEST_TEXTURE = 200;
+static const int CMD_INVALIDATE_TEXTURE = 210;
+static const int CMD_REQUEST_GEOMETRY = 300;
+static const int CMD_INVALIDATE_GEOMETRY = 310;
+static const int CMD_ANIMATION_STARTED = 400;
+static const int CMD_ANIMATION_FINISHED = 500;
+static const int CMD_PING = 600;
 
+// Constants
+static const int ANIMATION_SCALE_TIME = 200; // Time it takes to animate selected card, in ms
+static const float3 SELECTED_SCALE_FACTOR = { 0.2f, 0.2f, 0.2f }; // increase by this %
 
 // Debug flags
-bool debugCamera = false;
-bool debugPicking = false;
+bool debugCamera = false; // dumps ray/camera coordinate stuff
+bool debugPicking = false; // renders picking area on top of geometry
 
 // Exported variables. These will be reflected to Java set_* variables.
 Card_t *cards; // array of cards to draw
@@ -96,8 +99,11 @@
 static float bias; // rotation bias, in radians. Used for animation and dragging.
 static bool updateCamera;    // force a recompute of projection and lookat matrices
 static bool initialized;
-static float3 backgroundColor = { 0.5f, 0.5f, 0.5f };
+static float3 backgroundColor = { 0.0f, 0.0f, 0.0f };
 static const float FLT_MAX = 1.0e37;
+static int currentSelection = -1;
+static int64_t touchTime = -1;  // time of first touch (see doStart())
+static float touchBias = 0.0f; // bias on first touch
 
 // Default geometry when card.geometry is not set.
 static const float3 cardVertices[4] = {
@@ -121,10 +127,12 @@
 // Forward references
 static int intersectGeometry(Ray* ray, float *bestTime);
 static bool makeRayForPixelAt(Ray* ray, float x, float y);
+static float deltaTimeInSeconds(int64_t current);
 
 void init() {
     // initializers currently have a problem when the variables are exported, so initialize
     // globals here.
+    rsDebug("Renderscript: init()", 0);
     startAngle = 0.0f;
     slotCount = 10;
     visibleSlotCount = 1;
@@ -245,12 +253,24 @@
         cards[n].geometryState = STATE_INVALID;
 }
 
+static float3 getAnimatedScaleForSelected()
+{
+    int64_t dt = (rsUptimeMillis() - touchTime);
+    float fraction = (dt < ANIMATION_SCALE_TIME) ? (float) dt / ANIMATION_SCALE_TIME : 1.0f;
+    const float3 one = { 1.0f, 1.0f, 1.0f };
+    return one + fraction * SELECTED_SCALE_FACTOR;
+}
+
 static void getMatrixForCard(rs_matrix4x4* matrix, int i)
 {
     float theta = cardPosition(i);
     rsMatrixRotate(matrix, degrees(theta), 0, 1, 0);
     rsMatrixTranslate(matrix, radius, 0, 0);
     rsMatrixRotate(matrix, degrees(-theta + cardRotation), 0, 1, 0);
+    if (i == currentSelection) {
+        float3 scale = getAnimatedScaleForSelected();
+        rsMatrixScale(matrix, scale.x, scale.y, scale.z);
+    }
     // TODO: apply custom matrix for cards[i].geometry
 }
 
@@ -353,27 +373,30 @@
     }
     velocityTracker = 0.0f;
     velocityTrackerCount = 0;
+    touchTime = rsUptimeMillis();
+    touchBias = bias;
+    currentSelection = doSelection(x, y);
 }
 
 
 void doStop(float x, float y)
 {
+    int64_t currentTime = rsUptimeMillis();
     updateAllocationVars();
-
-    velocity = velocityTrackerCount > 0 ?
-            (velocityTracker / velocityTrackerCount) : 0.0f;  // avg velocity
-    if (fabs(velocity) > velocityThreshold) {
-        animating = true;
-        rsSendToClient(CMD_ANIMATION_STARTED);
+    if (currentSelection != -1 && (currentTime - touchTime) < ANIMATION_SCALE_TIME) {
+        rsDebug("HIT!", currentSelection);
+        int data[1];
+        data[0] = currentSelection;
+        rsSendToClientBlocking(CMD_CARD_SELECTED, data, sizeof(data));
     } else {
-        const int selection = doSelection(x, y);  // velocity too small; treat as a tap
-        if (selection != -1) {
-            rsDebug("HIT!", selection);
-            int data[1];
-            data[0] = selection;
-            rsSendToClient(CMD_CARD_SELECTED, data, sizeof(data));
+        velocity = velocityTrackerCount > 0 ?
+                    (velocityTracker / velocityTrackerCount) : 0.0f;  // avg velocity
+        if (fabs(velocity) > velocityThreshold) {
+            animating = true;
+            rsSendToClient(CMD_ANIMATION_STARTED);
         }
     }
+    currentSelection = -1;
     lastTime = rsUptimeMillis();
 }
 
@@ -395,6 +418,14 @@
         //    velocityTrackerCount = 1;
         //}
     }
+
+    // Drop current selection if user drags position +- a partial slot
+    if (currentSelection != -1) {
+        const float slotMargin = 0.5f * (2.0f * M_PI / slotCount);
+        if (fabs(touchBias - bias) > slotMargin) {
+            currentSelection = -1;
+        }
+    }
     lastTime = currentTime;
 }
 
@@ -431,6 +462,8 @@
     return true;
 }
 
+// Creates a ray for an Android pixel coordinate.
+// Note that the Y coordinate is opposite of GL rendering coordinates.
 static bool makeRayForPixelAt(Ray* ray, float x, float y)
 {
     if (debugCamera) {
@@ -444,7 +477,7 @@
     // TODO: pre-compute lowerLeftRay, du, dv to eliminate most of this math.
     if (true) {
         const float u = x / rsgGetWidth();
-        const float v = (y / rsgGetHeight());
+        const float v = 1.0f - (y / rsgGetHeight());
         const float aspect = (float) rsgGetWidth() / rsgGetHeight();
         const float tanfov2 = 2.0f * tan(radians(camera.fov / 2.0f));
         float3 dir = normalize(camera.at - camera.from);
@@ -537,9 +570,8 @@
 // This method computes the position of all the cards by updating bias based on a
 // simple physics model.
 // If the cards are still in motion, returns true.
-static bool updateNextPosition()
+static bool updateNextPosition(int64_t currentTime)
 {
-    int64_t currentTime = rsUptimeMillis();
     if (animating) {
         float dt = deltaTimeInSeconds(currentTime);
         if (dt <= 0.0f)
@@ -658,8 +690,7 @@
             // ask the host to remove the texture
             if (cards[i].textureState == STATE_LOADED) {
                 data[0] = i;
-                bool enqueued = true;
-                rsSendToClientBlocking(CMD_INVALIDATE_TEXTURE, data, sizeof(data));
+                bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data));
                 if (enqueued) {
                     cards[i].textureState = STATE_INVALID;
                 } else {
@@ -669,8 +700,7 @@
             // ask the host to remove the geometry
             if (cards[i].geometryState == STATE_LOADED) {
                 data[0] = i;
-                bool enqueued = true;
-                rsSendToClientBlocking(CMD_INVALIDATE_GEOMETRY, data, sizeof(data));
+                bool enqueued = rsSendToClient(CMD_INVALIDATE_GEOMETRY, data, sizeof(data));
                 if (enqueued) {
                     cards[i].geometryState = STATE_INVALID;
                 } else {
@@ -699,7 +729,7 @@
             if (makeRayForPixelAt(&ray, posX, posY)) {
                 float bestTime = FLT_MAX;
                 if (intersectGeometry(&ray, &bestTime) != -1) {
-                    rsgDrawSpriteScreenspace(posX, posY, 0.0f, 2.0f, 2.0f);
+                    rsgDrawSpriteScreenspace(posX, h - posY - 1, 0.0f, 2.0f, 2.0f);
                 }
             }
         }
@@ -707,8 +737,9 @@
 }
 
 int root() {
-    rsgClearDepth(1.0f);
+    int64_t currentTime = rsUptimeMillis();
 
+    rsgClearDepth(1.0f);
     rsgBindProgramVertex(vertexProgram);
     rsgBindProgramFragment(fragmentProgram);
     rsgBindProgramStore(programStore);
@@ -735,7 +766,11 @@
 
     updateCameraMatrix(rsgGetWidth(), rsgGetHeight());
 
-    bool stillAnimating = updateNextPosition();
+    const bool timeExpired = (currentTime - touchTime) > ANIMATION_SCALE_TIME;
+    if (timeExpired) {
+        //currentSelection = -1;
+    }
+    bool stillAnimating = updateNextPosition(currentTime) || !timeExpired;
 
     cullCards();
 
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index dba1cea..6f6d919 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -506,7 +506,7 @@
 static void readLocale(char* language, char* region)
 {
     char propLang[PROPERTY_VALUE_MAX], propRegn[PROPERTY_VALUE_MAX];
-    
+
     property_get("persist.sys.language", propLang, "");
     property_get("persist.sys.country", propRegn, "");
     if (*propLang == 0 && *propRegn == 0) {
@@ -701,6 +701,42 @@
         }
     }
 
+    /* enable poisoning of memory of freed objects */
+    property_get("dalvik.vm.gc.overwritefree", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:overwritefree";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.overwritefree should be 'true' or 'false'");
+    }
+
+    /* enable heap verification before each gc */
+    property_get("dalvik.vm.gc.preverify", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:preverify";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.preverify should be 'true' or 'false'");
+    }
+
+    /* enable heap verification after each gc */
+    property_get("dalvik.vm.gc.postverify", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:postverify";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.postverify should be 'true' or 'false'");
+    }
+
+    /* enable card table verification for partial gc */
+    property_get("dalvik.vm.gc.verifycardtable", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:verifycardtable";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.verifycardtable should be 'true' or 'false'");
+    }
+
     /* enable debugging; set suspend=y to pause during VM init */
 #ifdef HAVE_ANDROID_OS
     /* use android ADB transport */
@@ -748,16 +784,6 @@
     }
 
 #if defined(WITH_JIT)
-    /* Minimal profile threshold to trigger JIT compilation */
-    char jitThresholdBuf[sizeof("-Xjitthreshold:") + PROPERTY_VALUE_MAX];
-    property_get("dalvik.vm.jit.threshold", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        strcpy(jitThresholdBuf, "-Xjitthreshold:");
-        strcat(jitThresholdBuf, propBuf);
-        opt.optionString = jitThresholdBuf;
-        mOptions.add(opt);
-    }
-
     /* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */
     char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
     property_get("dalvik.vm.jit.op", propBuf, "");
@@ -768,16 +794,6 @@
         mOptions.add(opt);
     }
 
-    /*
-     * Reverse the polarity of dalvik.vm.jit.op and force interpreter-only
-     * for non-selected opcodes.
-     */
-    property_get("dalvik.vm.jit.includeop", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        opt.optionString = "-Xincludeselectedop";
-        mOptions.add(opt);
-    }
-
     /* Force interpreter-only mode for selected methods */
     char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
     property_get("dalvik.vm.jit.method", propBuf, "");
@@ -787,37 +803,6 @@
         opt.optionString = jitMethodBuf;
         mOptions.add(opt);
     }
-
-    /*
-     * Reverse the polarity of dalvik.vm.jit.method and force interpreter-only
-     * for non-selected methods.
-     */
-    property_get("dalvik.vm.jit.includemethod", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        opt.optionString = "-Xincludeselectedmethod";
-        mOptions.add(opt);
-    }
-
-    /*
-     * Enable profile collection on JIT'ed code.
-     */
-    property_get("dalvik.vm.jit.profile", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        opt.optionString = "-Xjitprofile";
-        mOptions.add(opt);
-    }
-
-    /*
-     * Disable optimizations by setting the corresponding bit to 1.
-     */
-    char jitOptBuf[sizeof("-Xjitdisableopt:") + PROPERTY_VALUE_MAX];
-    property_get("dalvik.vm.jit.disableopt", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        strcpy(jitOptBuf, "-Xjitdisableopt:");
-        strcat(jitOptBuf, propBuf);
-        opt.optionString = jitOptBuf;
-        mOptions.add(opt);
-    }
 #endif
 
     if (executionMode == kEMIntPortable) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 204bb74..578de6f 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -518,35 +518,48 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM)
-    : fEnv(env), fReportSizeToVM(reportSizeToVM) {}
+    : fReportSizeToVM(reportSizeToVM) {
+    if (env->GetJavaVM(&fVM) != JNI_OK) {
+        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
+        sk_throw();
+    }
+}
     
 bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
-    return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM);
+    JNIEnv* env = vm2env(fVM);
+    return GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, fReportSizeToVM);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
-    : fEnv(env), fTotalSize(0) {}
+    : fTotalSize(0) {
+    if (env->GetJavaVM(&fVM) != JNI_OK) {
+        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
+        sk_throw();
+    }
+}
 
 JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
+    JNIEnv* env = vm2env(fVM);
     jlong jtotalSize = fTotalSize;
-    fEnv->CallVoidMethod(gVMRuntime_singleton,
+    env->CallVoidMethod(gVMRuntime_singleton,
             gVMRuntime_trackExternalFreeMethodID,
             jtotalSize);
 }
 
 bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
     jlong jsize = memorySize;  // the VM wants longs for the size
-    bool r = fEnv->CallBooleanMethod(gVMRuntime_singleton,
+    JNIEnv* env = vm2env(fVM);
+    bool r = env->CallBooleanMethod(gVMRuntime_singleton,
             gVMRuntime_trackExternalAllocationMethodID,
             jsize);
-    if (GraphicsJNI::hasException(fEnv)) {
+    if (GraphicsJNI::hasException(env)) {
         return false;
     }
     if (!r) {
         LOGE("VM won't let us allocate %zd bytes\n", memorySize);
-        doThrowOOME(fEnv, "bitmap size exceeds VM budget");
+        doThrowOOME(env, "bitmap size exceeds VM budget");
         return false;
     }
     fTotalSize += memorySize;
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 8d6528b..1a43a3e 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -80,7 +80,7 @@
     virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
     
 private:
-    JNIEnv* fEnv;
+    JavaVM* fVM;
     bool fReportSizeToVM;
 };
 
@@ -92,7 +92,7 @@
     virtual bool reportMemory(size_t memorySize);
 
 private:
-    JNIEnv* fEnv;
+    JavaVM* fVM;
     size_t fTotalSize;
 };
 
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index 1239274..62c89fc 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -31,6 +31,7 @@
 
     jfieldID packageName;
     jfieldID version;
+    jfieldID flags;
 } gObbInfoClassInfo;
 
 static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
@@ -85,6 +86,8 @@
             "packageName", "Ljava/lang/String;");
     GET_FIELD_ID(gObbInfoClassInfo.version, gObbInfoClassInfo.clazz,
             "version", "I");
+    GET_FIELD_ID(gObbInfoClassInfo.flags, gObbInfoClassInfo.clazz,
+            "flags", "I");
 
     return AndroidRuntime::registerNativeMethods(env, "android/content/res/ObbScanner", gMethods,
             NELEM(gMethods));
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 17f5daf..f78f83c 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -453,30 +453,23 @@
 // return -1 if there was an error querying the buffer size.
 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
     jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
-    
-    size_t inputBuffSize = 0;
+
     LOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat);
-    
-    status_t result = AudioSystem::getInputBufferSize(
-                        sampleRateInHertz, 
-                        (audioFormat == javaAudioRecordFields.PCM16 ? 
-                            AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT), 
-                        nbChannels, &inputBuffSize);
-    switch(result) {
-    case(NO_ERROR):
-        if(inputBuffSize == 0) {
-            LOGV("Recording parameters are not supported: %dHz, %d channel(s), (java) format %d",
-                sampleRateInHertz, nbChannels, audioFormat);
-            return 0;
-        } else {
-            // the minimum buffer size is twice the hardware input buffer size
-            return 2*inputBuffSize;
-        }
-        break;
-    case(PERMISSION_DENIED):
-    default:
-        return -1; 
+
+    int frameCount = 0;
+    status_t result = AudioRecord::getMinFrameCount(&frameCount,
+            sampleRateInHertz,
+            (audioFormat == javaAudioRecordFields.PCM16 ?
+                AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT),
+            nbChannels);
+
+    if (result == BAD_VALUE) {
+        return 0;
     }
+    if (result != NO_ERROR) {
+        return -1;
+    }
+    return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1);
 }
 
 
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index c559670..9d215b7 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -782,29 +782,13 @@
 // returns -1 if there was an error querying the hardware.
 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,
     jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
-    int afSamplingRate;
-    int afFrameCount;
-    uint32_t afLatency;
-    
-    if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {
+
+    int frameCount = 0;
+    if (AudioTrack::getMinFrameCount(&frameCount, AudioSystem::DEFAULT,
+            sampleRateInHertz) != NO_ERROR) {
         return -1;
     }
-    if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {
-        return -1;
-    }
-    
-    if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {
-        return -1;
-    }
-    
-    // Ensure that buffer depth covers at least audio hardware latency
-    uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate);
-    if (minBufCount < 2) minBufCount = 2;
-    uint32_t minFrameCount = (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;
-    int minBuffSize = minFrameCount 
-            * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)
-            * nbChannels;
-    return minBuffSize;
+    return frameCount * nbChannels * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 4c6eced..98c03a6 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -34,7 +34,6 @@
 #include <SkiaShader.h>
 #include <SkiaColorFilter.h>
 #include <Rect.h>
-#include <ui/Rect.h>
 
 #include "TextLayout.h"
 
@@ -418,7 +417,7 @@
     { "nDrawTextRun",       "(ILjava/lang/String;IIIIFFII)V",
             (void*) android_view_GLES20Canvas_drawTextRun },
 
-    {   "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
+    { "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
             (void*) android_view_GLES20Canvas_getClipBounds },
 #endif
 };
diff --git a/core/res/res/raw/incognito_mode_start_page.html b/core/res/assets/webkit/incognito_mode_start_page.html
similarity index 100%
rename from core/res/res/raw/incognito_mode_start_page.html
rename to core/res/assets/webkit/incognito_mode_start_page.html
diff --git a/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png b/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png
deleted file mode 100644
index 97e6806..0000000
--- a/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off.png b/core/res/res/drawable-hdpi/btn_check_off.png
index 911e1aa..aad9ef7 100644
--- a/core/res/res/drawable-hdpi/btn_check_off.png
+++ b/core/res/res/drawable-hdpi/btn_check_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable.png b/core/res/res/drawable-hdpi/btn_check_off_disable.png
index d72e2b9..eaee9e0 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_disable.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
index d72e2b9..6d2c293 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_dark.png
new file mode 100644
index 0000000..d72e2b9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png
rename to core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_dark.png
new file mode 100644
index 0000000..d72e2b9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_off_disable_light.png
rename to core/res/res/drawable-hdpi/btn_check_off_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_holo_dark.png
new file mode 100644
index 0000000..911e1aa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_light.png b/core/res/res/drawable-hdpi/btn_check_off_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_off_light.png
rename to core/res/res/drawable-hdpi/btn_check_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed.png b/core/res/res/drawable-hdpi/btn_check_off_pressed.png
index 08f41812..1c442e9 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_dark.png
new file mode 100644
index 0000000..08f41812
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed_light.png b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_off_pressed_light.png
rename to core/res/res/drawable-hdpi/btn_check_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected.png b/core/res/res/drawable-hdpi/btn_check_off_selected.png
index 264f102..b852b2c 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_selected.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_dark.png
new file mode 100644
index 0000000..264f102
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected_light.png b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_off_selected_light.png
rename to core/res/res/drawable-hdpi/btn_check_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on.png b/core/res/res/drawable-hdpi/btn_check_on.png
index 5541c67..cd5c181 100644
--- a/core/res/res/drawable-hdpi/btn_check_on.png
+++ b/core/res/res/drawable-hdpi/btn_check_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable.png b/core/res/res/drawable-hdpi/btn_check_on_disable.png
index 7805458..b4fc51a 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_disable.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
index 7805458..bf34647 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png
new file mode 100644
index 0000000..7805458
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png
rename to core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_dark.png
new file mode 100644
index 0000000..7805458
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_on_disable_light.png
rename to core/res/res/drawable-hdpi/btn_check_on_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_holo_dark.png
new file mode 100644
index 0000000..5541c67
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_light.png b/core/res/res/drawable-hdpi/btn_check_on_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_on_light.png
rename to core/res/res/drawable-hdpi/btn_check_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed.png b/core/res/res/drawable-hdpi/btn_check_on_pressed.png
index 37e3953..fa5c7a2 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png
new file mode 100644
index 0000000..37e3953
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_light.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_on_pressed_light.png
rename to core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected.png b/core/res/res/drawable-hdpi/btn_check_on_selected.png
index a0beac4..a6a21ad 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_selected.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_dark.png
new file mode 100644
index 0000000..a0beac4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected_light.png b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_on_selected_light.png
rename to core/res/res/drawable-hdpi/btn_check_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png b/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png
deleted file mode 100644
index 45c5c6a..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off.png b/core/res/res/drawable-hdpi/btn_radio_off.png
index 301c97d..c0b14aa 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_off_holo_dark.png
new file mode 100644
index 0000000..301c97d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_light.png b/core/res/res/drawable-hdpi/btn_radio_off_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_off_light.png
rename to core/res/res/drawable-hdpi/btn_radio_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
index 5e6ef2b..3189581 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_dark.png
new file mode 100644
index 0000000..5e6ef2b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png
rename to core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected.png b/core/res/res/drawable-hdpi/btn_radio_off_selected.png
index d11ae85..f337703 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_selected.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_dark.png
new file mode 100644
index 0000000..d11ae85
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected_light.png b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_off_selected_light.png
rename to core/res/res/drawable-hdpi/btn_radio_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on.png b/core/res/res/drawable-hdpi/btn_radio_on.png
index 5b0dbe8..c90d2eb 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_on_holo_dark.png
new file mode 100644
index 0000000..5b0dbe8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_light.png b/core/res/res/drawable-hdpi/btn_radio_on_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_on_light.png
rename to core/res/res/drawable-hdpi/btn_radio_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
index c3a0d48..d79450b8 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_dark.png
new file mode 100644
index 0000000..c3a0d48
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png
rename to core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected.png b/core/res/res/drawable-hdpi/btn_radio_on_selected.png
index 6c05f47..db50c43 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_selected.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_dark.png
new file mode 100644
index 0000000..6c05f47
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected_light.png b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_light.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_on_selected_light.png
rename to core/res/res/drawable-hdpi/btn_radio_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png b/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png
index 99a67b9..41b776b 100644
--- a/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png
+++ b/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png b/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png
index cfe258b..eb75a22 100644
--- a/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png
+++ b/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png
index 338d840..55a5e53 100644
--- a/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png
+++ b/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png
index 9444f0d..60e2cb2 100644
--- a/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png
+++ b/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png
index 63859f7..cf34613 100644
--- a/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png
+++ b/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_bright.9.png b/core/res/res/drawable-hdpi/divider_vertical_bright.9.png
index 99a67b9..41b776b 100644
--- a/core/res/res/drawable-hdpi/divider_vertical_bright.9.png
+++ b/core/res/res/drawable-hdpi/divider_vertical_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_bright_opaque.9.png b/core/res/res/drawable-hdpi/divider_vertical_bright_opaque.9.png
index 5c537ee..eb75a22 100644
--- a/core/res/res/drawable-hdpi/divider_vertical_bright_opaque.9.png
+++ b/core/res/res/drawable-hdpi/divider_vertical_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_dark.9.png b/core/res/res/drawable-hdpi/divider_vertical_dark.9.png
index 702b878..55a5e53 100644
--- a/core/res/res/drawable-hdpi/divider_vertical_dark.9.png
+++ b/core/res/res/drawable-hdpi/divider_vertical_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_dark_opaque.9.png b/core/res/res/drawable-hdpi/divider_vertical_dark_opaque.9.png
index 8f35315..60e2cb2 100644
--- a/core/res/res/drawable-hdpi/divider_vertical_dark_opaque.9.png
+++ b/core/res/res/drawable-hdpi/divider_vertical_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_default.9.png b/core/res/res/drawable-hdpi/list_selector_background_default.9.png
new file mode 100644
index 0000000..25c6241
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_default_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_default_light.9.png
new file mode 100644
index 0000000..c3e69f0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_default_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png b/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
index c40233e..ab377d8 100644
--- a/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_disabled_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_disabled_light.9.png
new file mode 100644
index 0000000..8edc9cd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_disabled_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_focus.9.png b/core/res/res/drawable-hdpi/list_selector_background_focus.9.png
deleted file mode 100644
index d8e16b99..0000000
--- a/core/res/res/drawable-hdpi/list_selector_background_focus.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_focused.9.png b/core/res/res/drawable-hdpi/list_selector_background_focused.9.png
new file mode 100644
index 0000000..60bb454
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_focused_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_focused_light.9.png
new file mode 100644
index 0000000..60bb454
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_focused_selected.9.png b/core/res/res/drawable-hdpi/list_selector_background_focused_selected.9.png
new file mode 100644
index 0000000..b527da15
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_focused_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png b/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
index 1676ca7..bd74426 100644
--- a/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_longpress_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_longpress_light.9.png
new file mode 100644
index 0000000..fc2ba2e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_longpress_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png b/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
index ba79cf7..d18d6f7 100644
--- a/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_pressed_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_pressed_light.9.png
new file mode 100644
index 0000000..00786e5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_selected.9.png b/core/res/res/drawable-hdpi/list_selector_background_selected.9.png
new file mode 100644
index 0000000..cbf50b3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_selected_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_selected_light.9.png
new file mode 100644
index 0000000..34ea86e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_selected_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png b/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png
deleted file mode 100644
index 79367b8..0000000
--- a/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off.png b/core/res/res/drawable-mdpi/btn_check_off.png
index 5e44c29..56d3861 100644
--- a/core/res/res/drawable-mdpi/btn_check_off.png
+++ b/core/res/res/drawable-mdpi/btn_check_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable.png b/core/res/res/drawable-mdpi/btn_check_off_disable.png
index a603fb1..e012afd 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
index a603fb1..0837bbd 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_dark.png
new file mode 100644
index 0000000..a603fb1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png
rename to core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_dark.png
new file mode 100644
index 0000000..a603fb1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_off_disable_light.png
rename to core/res/res/drawable-mdpi/btn_check_off_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_holo_dark.png
new file mode 100644
index 0000000..5e44c29
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_light.png b/core/res/res/drawable-mdpi/btn_check_off_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_off_light.png
rename to core/res/res/drawable-mdpi/btn_check_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed.png b/core/res/res/drawable-mdpi/btn_check_off_pressed.png
index 611bb1d..984dfd7 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_dark.png
new file mode 100644
index 0000000..611bb1d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed_light.png b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_off_pressed_light.png
rename to core/res/res/drawable-mdpi/btn_check_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected.png b/core/res/res/drawable-mdpi/btn_check_off_selected.png
index aa28df2..20842d4 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_dark.png
new file mode 100644
index 0000000..aa28df2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected_light.png b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_off_selected_light.png
rename to core/res/res/drawable-mdpi/btn_check_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on.png b/core/res/res/drawable-mdpi/btn_check_on.png
index 130d562..791ac1d 100644
--- a/core/res/res/drawable-mdpi/btn_check_on.png
+++ b/core/res/res/drawable-mdpi/btn_check_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable.png b/core/res/res/drawable-mdpi/btn_check_on_disable.png
index f19972a..6cb02f3 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
index f19972a..8a73b33 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png
new file mode 100644
index 0000000..f19972a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png
rename to core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_dark.png
new file mode 100644
index 0000000..f19972a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_on_disable_light.png
rename to core/res/res/drawable-mdpi/btn_check_on_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_holo_dark.png
new file mode 100644
index 0000000..130d562
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_light.png b/core/res/res/drawable-mdpi/btn_check_on_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_on_light.png
rename to core/res/res/drawable-mdpi/btn_check_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed.png b/core/res/res/drawable-mdpi/btn_check_on_pressed.png
index df753f5..300d64a 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png
new file mode 100644
index 0000000..df753f5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_light.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_on_pressed_light.png
rename to core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected.png b/core/res/res/drawable-mdpi/btn_check_on_selected.png
index 7586881..0b36adb 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_dark.png
new file mode 100644
index 0000000..7586881
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected_light.png b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_on_selected_light.png
rename to core/res/res/drawable-mdpi/btn_check_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png b/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png
deleted file mode 100644
index 16e8939..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off.png b/core/res/res/drawable-mdpi/btn_radio_off.png
index 16c1c6b..407632b 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_off_holo_dark.png
new file mode 100644
index 0000000..16c1c6b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_light.png b/core/res/res/drawable-mdpi/btn_radio_off_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_off_light.png
rename to core/res/res/drawable-mdpi/btn_radio_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
index b25217b..d6d8a9d 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_dark.png
new file mode 100644
index 0000000..b25217b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png
rename to core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected.png b/core/res/res/drawable-mdpi/btn_radio_off_selected.png
index bef7572..53f3e87 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_dark.png
new file mode 100644
index 0000000..bef7572
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected_light.png b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_off_selected_light.png
rename to core/res/res/drawable-mdpi/btn_radio_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on.png b/core/res/res/drawable-mdpi/btn_radio_on.png
index 4ed7471..25a3ccc 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_on_holo_dark.png
new file mode 100644
index 0000000..4ed7471
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_light.png b/core/res/res/drawable-mdpi/btn_radio_on_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_on_light.png
rename to core/res/res/drawable-mdpi/btn_radio_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
index 7cf91c6..c904a35 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_dark.png
new file mode 100644
index 0000000..7cf91c6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png
rename to core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected.png b/core/res/res/drawable-mdpi/btn_radio_on_selected.png
index 56f6f5b..78e1fc0 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_dark.png
new file mode 100644
index 0000000..56f6f5b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected_light.png b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_light.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_on_selected_light.png
rename to core/res/res/drawable-mdpi/btn_radio_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png b/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png
index 395227a..41b776b 100644
--- a/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png b/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png
index 5c537ee..eb75a22 100644
--- a/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png b/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png
index bf45e38..55a5e53 100644
--- a/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png b/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png
index 9444f0d..60e2cb2 100644
--- a/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png b/core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png
index 08838ca..cf34613 100644
--- a/core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_bright.9.png b/core/res/res/drawable-mdpi/divider_vertical_bright.9.png
index 395227a..41b776b 100644
--- a/core/res/res/drawable-mdpi/divider_vertical_bright.9.png
+++ b/core/res/res/drawable-mdpi/divider_vertical_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png b/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png
index 5c537ee..eb75a22 100644
--- a/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png
+++ b/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_dark.9.png b/core/res/res/drawable-mdpi/divider_vertical_dark.9.png
index 702b878..55a5e53 100644
--- a/core/res/res/drawable-mdpi/divider_vertical_dark.9.png
+++ b/core/res/res/drawable-mdpi/divider_vertical_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png b/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png
index 8f35315..60e2cb2 100644
--- a/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png
+++ b/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_default.9.png b/core/res/res/drawable-mdpi/list_selector_background_default.9.png
new file mode 100644
index 0000000..cac71b0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_default_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_default_light.9.png
new file mode 100644
index 0000000..bdedffd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_default_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png b/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
index bf970b0..60f19fe 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_disabled_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_disabled_light.9.png
new file mode 100644
index 0000000..bc992612
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_disabled_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_focus.9.png b/core/res/res/drawable-mdpi/list_selector_background_focus.9.png
deleted file mode 100644
index c3e24158..0000000
--- a/core/res/res/drawable-mdpi/list_selector_background_focus.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_focused.9.png b/core/res/res/drawable-mdpi/list_selector_background_focused.9.png
new file mode 100644
index 0000000..5b1f195
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_focused_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_focused_light.9.png
new file mode 100644
index 0000000..5b1f195
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_focused_selected.9.png b/core/res/res/drawable-mdpi/list_selector_background_focused_selected.9.png
new file mode 100644
index 0000000..e5b0c1d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_focused_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png b/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
index 5cbb251..7817667 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_longpress_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_longpress_light.9.png
new file mode 100644
index 0000000..646fc69
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_longpress_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png b/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
index 02b4e9a..1531d9d 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_pressed_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_pressed_light.9.png
new file mode 100644
index 0000000..fdf6f49
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_selected.9.png b/core/res/res/drawable-mdpi/list_selector_background_selected.9.png
new file mode 100644
index 0000000..a4ac1e3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_selected_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_selected_light.9.png
new file mode 100644
index 0000000..d086194
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_selected_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_light.xml b/core/res/res/drawable/btn_check_holo_dark.xml
similarity index 72%
copy from core/res/res/drawable/btn_check_light.xml
copy to core/res/res/drawable/btn_check_holo_dark.xml
index 85f119a..fd85d72 100644
--- a/core/res/res/drawable/btn_check_light.xml
+++ b/core/res/res/drawable/btn_check_holo_dark.xml
@@ -17,49 +17,49 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
     <!-- Enabled states -->
-
+        
     <item android:state_checked="true" android:state_window_focused="false"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_on_light" />
+          android:drawable="@drawable/btn_check_on_holo_dark" />
     <item android:state_checked="false" android:state_window_focused="false"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_off_light" />
+          android:drawable="@drawable/btn_check_off_holo_dark" />
 
     <item android:state_checked="true" android:state_pressed="true"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_on_pressed_light" />
+          android:drawable="@drawable/btn_check_on_pressed_holo_dark" />
     <item android:state_checked="false" android:state_pressed="true"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_off_pressed_light" />
+          android:drawable="@drawable/btn_check_off_pressed_holo_dark" />
 
     <item android:state_checked="true" android:state_focused="true"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_on_selected_light" />
+          android:drawable="@drawable/btn_check_on_selected_holo_dark" />
     <item android:state_checked="false" android:state_focused="true"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_off_selected_light" />
+          android:drawable="@drawable/btn_check_off_selected_holo_dark" />
 
     <item android:state_checked="false"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_off_light" />
+          android:drawable="@drawable/btn_check_off_holo_dark" />
     <item android:state_checked="true"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_on_light" />
+          android:drawable="@drawable/btn_check_on_holo_dark" />
 
 
     <!-- Disabled states -->
 
     <item android:state_checked="true" android:state_window_focused="false"
-          android:drawable="@drawable/btn_check_on_disable_light" />
+          android:drawable="@drawable/btn_check_on_disable_holo_dark" />
     <item android:state_checked="false" android:state_window_focused="false"
-          android:drawable="@drawable/btn_check_off_disable_light" />
+          android:drawable="@drawable/btn_check_off_disable_holo_dark" />
 
     <item android:state_checked="true" android:state_focused="true"
-          android:drawable="@drawable/btn_check_on_disable_focused_light" />
+          android:drawable="@drawable/btn_check_on_disable_focused_holo_dark" />
     <item android:state_checked="false" android:state_focused="true"
-          android:drawable="@drawable/btn_check_off_disable_focused_light" />
+          android:drawable="@drawable/btn_check_off_disable_focused_holo_dark" />
 
-    <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disable_light" />
-    <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disable_light" />
+    <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disable_holo_dark" />
+    <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disable_holo_dark" />
 
 </selector>
diff --git a/core/res/res/drawable/btn_check_light.xml b/core/res/res/drawable/btn_check_holo_light.xml
similarity index 72%
rename from core/res/res/drawable/btn_check_light.xml
rename to core/res/res/drawable/btn_check_holo_light.xml
index 85f119a..4fb16fa 100644
--- a/core/res/res/drawable/btn_check_light.xml
+++ b/core/res/res/drawable/btn_check_holo_light.xml
@@ -17,49 +17,49 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
     <!-- Enabled states -->
-
+        
     <item android:state_checked="true" android:state_window_focused="false"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_on_light" />
+          android:drawable="@drawable/btn_check_on_holo_light" />
     <item android:state_checked="false" android:state_window_focused="false"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_off_light" />
+          android:drawable="@drawable/btn_check_off_holo_light" />
 
     <item android:state_checked="true" android:state_pressed="true"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_on_pressed_light" />
+          android:drawable="@drawable/btn_check_on_pressed_holo_light" />
     <item android:state_checked="false" android:state_pressed="true"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_off_pressed_light" />
+          android:drawable="@drawable/btn_check_off_pressed_holo_light" />
 
     <item android:state_checked="true" android:state_focused="true"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_on_selected_light" />
+          android:drawable="@drawable/btn_check_on_selected_holo_light" />
     <item android:state_checked="false" android:state_focused="true"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_off_selected_light" />
+          android:drawable="@drawable/btn_check_off_selected_holo_light" />
 
     <item android:state_checked="false"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_off_light" />
+          android:drawable="@drawable/btn_check_off_holo_light" />
     <item android:state_checked="true"
           android:state_enabled="true"
-          android:drawable="@drawable/btn_check_on_light" />
+          android:drawable="@drawable/btn_check_on_holo_light" />
 
 
     <!-- Disabled states -->
 
     <item android:state_checked="true" android:state_window_focused="false"
-          android:drawable="@drawable/btn_check_on_disable_light" />
+          android:drawable="@drawable/btn_check_on_disable_holo_light" />
     <item android:state_checked="false" android:state_window_focused="false"
-          android:drawable="@drawable/btn_check_off_disable_light" />
+          android:drawable="@drawable/btn_check_off_disable_holo_light" />
 
     <item android:state_checked="true" android:state_focused="true"
-          android:drawable="@drawable/btn_check_on_disable_focused_light" />
+          android:drawable="@drawable/btn_check_on_disable_focused_holo_light" />
     <item android:state_checked="false" android:state_focused="true"
-          android:drawable="@drawable/btn_check_off_disable_focused_light" />
+          android:drawable="@drawable/btn_check_off_disable_focused_holo_light" />
 
-    <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disable_light" />
-    <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disable_light" />
+    <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disable_holo_light" />
+    <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disable_holo_light" />
 
 </selector>
diff --git a/core/res/res/drawable/btn_radio_light.xml b/core/res/res/drawable/btn_radio_holo_dark.xml
similarity index 72%
rename from core/res/res/drawable/btn_radio_light.xml
rename to core/res/res/drawable/btn_radio_holo_dark.xml
index 51c930b..8984f6d 100644
--- a/core/res/res/drawable/btn_radio_light.xml
+++ b/core/res/res/drawable/btn_radio_holo_dark.xml
@@ -16,20 +16,20 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_checked="true" android:state_window_focused="false"
-          android:drawable="@drawable/btn_radio_on_light" />
+          android:drawable="@drawable/btn_radio_on_holo_dark" />
     <item android:state_checked="false" android:state_window_focused="false"
-          android:drawable="@drawable/btn_radio_off_light" />
-
+          android:drawable="@drawable/btn_radio_off_holo_dark" />
+          
     <item android:state_checked="true" android:state_pressed="true"
-          android:drawable="@drawable/btn_radio_on_pressed_light" />
+          android:drawable="@drawable/btn_radio_on_pressed_holo_dark" />
     <item android:state_checked="false" android:state_pressed="true"
-          android:drawable="@drawable/btn_radio_off_pressed_light" />
+          android:drawable="@drawable/btn_radio_off_pressed_holo_dark" />
 
     <item android:state_checked="true" android:state_focused="true"
-          android:drawable="@drawable/btn_radio_on_selected_light" />
+          android:drawable="@drawable/btn_radio_on_selected_holo_dark" />
     <item android:state_checked="false" android:state_focused="true"
-          android:drawable="@drawable/btn_radio_off_selected_light" />
+          android:drawable="@drawable/btn_radio_off_selected_holo_dark" />
 
-    <item android:state_checked="false" android:drawable="@drawable/btn_radio_off_light" />
-    <item android:state_checked="true" android:drawable="@drawable/btn_radio_on_light" />
+    <item android:state_checked="false" android:drawable="@drawable/btn_radio_off_holo_dark" />
+    <item android:state_checked="true" android:drawable="@drawable/btn_radio_on_holo_dark" />
 </selector>
diff --git a/core/res/res/drawable/btn_radio_light.xml b/core/res/res/drawable/btn_radio_holo_light.xml
similarity index 72%
copy from core/res/res/drawable/btn_radio_light.xml
copy to core/res/res/drawable/btn_radio_holo_light.xml
index 51c930b..001508c 100644
--- a/core/res/res/drawable/btn_radio_light.xml
+++ b/core/res/res/drawable/btn_radio_holo_light.xml
@@ -16,20 +16,20 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_checked="true" android:state_window_focused="false"
-          android:drawable="@drawable/btn_radio_on_light" />
+          android:drawable="@drawable/btn_radio_on_holo_light" />
     <item android:state_checked="false" android:state_window_focused="false"
-          android:drawable="@drawable/btn_radio_off_light" />
-
+          android:drawable="@drawable/btn_radio_off_holo_light" />
+          
     <item android:state_checked="true" android:state_pressed="true"
-          android:drawable="@drawable/btn_radio_on_pressed_light" />
+          android:drawable="@drawable/btn_radio_on_pressed_holo_light" />
     <item android:state_checked="false" android:state_pressed="true"
-          android:drawable="@drawable/btn_radio_off_pressed_light" />
+          android:drawable="@drawable/btn_radio_off_pressed_holo_light" />
 
     <item android:state_checked="true" android:state_focused="true"
-          android:drawable="@drawable/btn_radio_on_selected_light" />
+          android:drawable="@drawable/btn_radio_on_selected_holo_light" />
     <item android:state_checked="false" android:state_focused="true"
-          android:drawable="@drawable/btn_radio_off_selected_light" />
+          android:drawable="@drawable/btn_radio_off_selected_holo_light" />
 
-    <item android:state_checked="false" android:drawable="@drawable/btn_radio_off_light" />
-    <item android:state_checked="true" android:drawable="@drawable/btn_radio_on_light" />
+    <item android:state_checked="false" android:drawable="@drawable/btn_radio_off_holo_light" />
+    <item android:state_checked="true" android:drawable="@drawable/btn_radio_on_holo_light" />
 </selector>
diff --git a/core/res/res/drawable/list_selected_background.xml b/core/res/res/drawable/list_selected_background.xml
new file mode 100644
index 0000000..2bb6594
--- /dev/null
+++ b/core/res/res/drawable/list_selected_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_focused="true"   android:state_checked="true" android:drawable="@drawable/list_selector_background_focused_selected" />
+    <item android:state_focused="false"  android:state_checked="true" android:drawable="@drawable/list_selector_background_selected" />
+
+</selector>
diff --git a/core/res/res/drawable/list_selected_background_light.xml b/core/res/res/drawable/list_selected_background_light.xml
new file mode 100644
index 0000000..15fc1cb
--- /dev/null
+++ b/core/res/res/drawable/list_selected_background_light.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_focused="true"   android:state_checked="true" android:drawable="@drawable/list_selector_background_focused_selected" />
+    <item android:state_focused="false"  android:state_checked="true" android:drawable="@drawable/list_selector_background_selected_light" />
+
+</selector>
diff --git a/core/res/res/drawable/list_selector_background.xml b/core/res/res/drawable/list_selector_background.xml
index bca996c..0a51152 100644
--- a/core/res/res/drawable/list_selector_background.xml
+++ b/core/res/res/drawable/list_selector_background.xml
@@ -16,22 +16,14 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <item android:state_window_focused="false"
-        android:drawable="@color/transparent" />
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
 
     <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
-    <item android:state_focused="true" android:state_enabled="false"
-        android:state_pressed="true"
-        android:drawable="@drawable/list_selector_background_disabled" />
-    <item android:state_focused="true" android:state_enabled="false"
-        android:drawable="@drawable/list_selector_background_disabled" />
-
-    <item android:state_focused="true" android:state_pressed="true"
-        android:drawable="@drawable/list_selector_background_transition" />
-    <item android:state_focused="false" android:state_pressed="true"
-        android:drawable="@drawable/list_selector_background_transition" />
-
-    <item android:state_focused="true"
-        android:drawable="@drawable/list_selector_background_focus" />
-
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_background_disabled" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_background_disabled" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/list_selector_background_focused" />
+    <item                                                                                          android:drawable="@drawable/list_selector_background_default" />
+    
 </selector>
diff --git a/core/res/res/drawable/list_selector_background_light.xml b/core/res/res/drawable/list_selector_background_light.xml
new file mode 100644
index 0000000..9b7980c
--- /dev/null
+++ b/core/res/res/drawable/list_selector_background_light.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_background_disabled_light" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_background_disabled_light" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_light" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_light" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/list_selector_background_focused_light" />
+    <item                                                                                          android:drawable="@drawable/list_selector_background_default_light" />
+
+</selector>
diff --git a/core/res/res/drawable/list_selector_background_transition_light.xml b/core/res/res/drawable/list_selector_background_transition_light.xml
new file mode 100644
index 0000000..634f895
--- /dev/null
+++ b/core/res/res/drawable/list_selector_background_transition_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@android:drawable/list_selector_background_pressed_light"  />
+    <item android:drawable="@android:drawable/list_selector_background_longpress_light"  />
+</transition>
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index eeba18e..fe950b2 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -29,12 +29,25 @@
         android:layout_height="0px"
         android:layout_weight="1">
 
-        <ListView android:id="@android:id/list"
+        <LinearLayout
+            android:orientation="vertical"
             android:layout_width="0px"
             android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:drawSelectorOnTop="false"
-            android:scrollbarAlwaysDrawVerticalTrack="true" />
+            android:layout_weight="1">
+
+            <ListView android:id="@android:id/list"
+                android:layout_width="match_parent"
+                android:layout_height="0px"
+                android:layout_weight="1"
+                android:drawSelectorOnTop="false"
+                android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+            <FrameLayout android:id="@+id/list_footer"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="0" />
+
+        </LinearLayout>
 
         <FrameLayout android:id="@+id/prefs"
                 android:layout_width="0px"
diff --git a/core/res/res/layout/recent_apps_activity.xml b/core/res/res/layout/recent_apps_activity.xml
new file mode 100644
index 0000000..d962339
--- /dev/null
+++ b/core/res/res/layout/recent_apps_activity.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <!-- Title -->
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="#80FFFFFF"
+        android:textStyle="bold"
+        android:singleLine="true"
+        android:text="@android:string/recent_tasks_title"
+        android:visibility="gone"/>
+
+    <!-- This is only intended to be visible when carousel is invisible -->
+    <TextView
+        android:id="@+id/no_applications_message"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:text="@android:string/no_recent_tasks"
+        android:visibility="gone"/>
+
+    <com.android.internal.widget.CarouselView
+        android:id="@+id/carousel"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1">
+    </com.android.internal.widget.CarouselView>
+
+</LinearLayout>
diff --git a/core/res/res/layout/select_dialog_multichoice.xml b/core/res/res/layout/select_dialog_multichoice.xml
index 5785d3b..b646a4c 100644
--- a/core/res/res/layout/select_dialog_multichoice.xml
+++ b/core/res/res/layout/select_dialog_multichoice.xml
@@ -24,7 +24,7 @@
     android:gravity="center_vertical"
     android:paddingLeft="12dip"
     android:paddingRight="7dip"
-    android:checkMark="@android:drawable/btn_check"
+    android:checkMark="?android:attr/listChoiceIndicatorMultiple"
     android:ellipsize="marquee"
 />
 
diff --git a/core/res/res/layout/select_dialog_singlechoice.xml b/core/res/res/layout/select_dialog_singlechoice.xml
index 3560fee..c3c2073 100644
--- a/core/res/res/layout/select_dialog_singlechoice.xml
+++ b/core/res/res/layout/select_dialog_singlechoice.xml
@@ -24,6 +24,6 @@
     android:gravity="center_vertical"
     android:paddingLeft="12dip"
     android:paddingRight="7dip"
-    android:checkMark="@android:drawable/btn_radio"
+    android:checkMark="?android:attr/listChoiceIndicatorSingle"
     android:ellipsize="marquee"
 />
diff --git a/core/res/res/layout/simple_selectable_list_item.xml b/core/res/res/layout/simple_selectable_list_item.xml
new file mode 100644
index 0000000..518bcd0
--- /dev/null
+++ b/core/res/res/layout/simple_selectable_list_item.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:textAppearance="?android:attr/textAppearanceLarge"
+    android:gravity="center_vertical"
+    android:background="?android:attr/listChoiceBackgroundIndicator"
+    android:paddingLeft="6dip"
+    android:paddingRight="9dip"
+/>
diff --git a/core/res/res/raw-rm/loaderror.html b/core/res/res/raw-rm/loaderror.html
new file mode 100644
index 0000000..8e4a3fe
--- /dev/null
+++ b/core/res/res/raw-rm/loaderror.html
@@ -0,0 +1,18 @@
+<html>
+    <head>
+        <title>Pagina d'internet betg disponibla</title>
+        <style type="text/css">
+            body { margin-top: 0px; padding-top: 0px; }
+            h2   { margin-top: 5px; padding-top: 0px; }
+        </style>
+
+        <body>
+
+            <img src="file:///android_asset/webkit/android-weberror.png" align="top" />
+            <h2>Pagina d'internet betg disponibla</h2>
+            <p>Impussibel da chargiar la pagina d'internet sut <a href="%s">%s</a>. Errur:</p>
+            <!-- The %e is replaced by a localized error string -->
+            <p>%e</p>
+        </body>
+    </head>
+</html>
diff --git a/core/res/res/raw-rm/nodomain.html b/core/res/res/raw-rm/nodomain.html
new file mode 100644
index 0000000..1e2833b
--- /dev/null
+++ b/core/res/res/raw-rm/nodomain.html
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <title>Pagina d'internet betg disponibla</title>
+        <style type="text/css">
+            body { margin-top: 0px; padding-top: 0px; }
+            h2   { margin-top: 5px; padding-top: 0px; }
+        </style>
+
+        <body>
+
+            <img src="file:///android_asset/webkit/android-weberror.png" align="top" />
+            <h2>Pagina d'internet betg disponibla</h2>
+            <p>La pagina d'internet sut <a href="%s">%s</a> &egrave; eventualmain deactivada temporarmain u spustada definitivamain ad ina nova adressa da web.</p>
+
+            <p><b>Qua in p&egrave;r propostas:</b></p>
+            <ul>
+                <li>Controllai che Voss apparat recepescha in signal e dispona d'ina connexiun da datas.</li>
+                <li>Chargiai pli tard danovamain la pagina d'internet.</li>
+                <li>Chargiai ina copia da la pagina da web memorisada en il cache da Google.</li>
+            </ul>
+        </body>
+    </head>
+</html>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 3de378b..1cbfbba 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1079,11 +1079,9 @@
     <skip />
     <!-- no translation found for cut (5845613239192595662) -->
     <skip />
-    <!-- no translation found for cutAll (4474519683293791451) -->
     <skip />
     <!-- no translation found for copy (8603721575469529820) -->
     <skip />
-    <!-- no translation found for copyAll (4777548804630476932) -->
     <skip />
     <!-- no translation found for paste (6458036735811828538) -->
     <skip />
diff --git a/core/res/res/values-en-rSG/strings.xml b/core/res/res/values-en-rSG/strings.xml
index 2ec6b0b..09a8490 100644
--- a/core/res/res/values-en-rSG/strings.xml
+++ b/core/res/res/values-en-rSG/strings.xml
@@ -1074,11 +1074,9 @@
     <skip />
     <!-- no translation found for cut (5845613239192595662) -->
     <skip />
-    <!-- no translation found for cutAll (4474519683293791451) -->
     <skip />
     <!-- no translation found for copy (8603721575469529820) -->
     <skip />
-    <!-- no translation found for copyAll (4777548804630476932) -->
     <skip />
     <!-- no translation found for paste (6458036735811828538) -->
     <skip />
diff --git a/core/res/res/values-en-rUS/strings.xml b/core/res/res/values-en-rUS/strings.xml
index 05f30fc..fdc0d69 100644
--- a/core/res/res/values-en-rUS/strings.xml
+++ b/core/res/res/values-en-rUS/strings.xml
@@ -1115,11 +1115,9 @@
     <skip />
     <!-- no translation found for cut (5845613239192595662) -->
     <skip />
-    <!-- no translation found for cutAll (4474519683293791451) -->
     <skip />
     <!-- no translation found for copy (8603721575469529820) -->
     <skip />
-    <!-- no translation found for copyAll (4777548804630476932) -->
     <skip />
     <!-- no translation found for paste (6458036735811828538) -->
     <skip />
diff --git a/core/res/res/values-rm/donottranslate-cldr.xml b/core/res/res/values-rm/donottranslate-cldr.xml
new file mode 100644
index 0000000..4b9f9db
--- /dev/null
+++ b/core/res/res/values-rm/donottranslate-cldr.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">schaner</string>
+    <string name="month_long_standalone_february">favrer</string>
+    <string name="month_long_standalone_march">mars</string>
+    <string name="month_long_standalone_april">avrigl</string>
+    <string name="month_long_standalone_may">matg</string>
+    <string name="month_long_standalone_june">zercladur</string>
+    <string name="month_long_standalone_july">fanadur</string>
+    <string name="month_long_standalone_august">avust</string>
+    <string name="month_long_standalone_september">settember</string>
+    <string name="month_long_standalone_october">october</string>
+    <string name="month_long_standalone_november">november</string>
+    <string name="month_long_standalone_december">december</string>
+
+    <string name="month_long_january">schaner</string>
+    <string name="month_long_february">favrer</string>
+    <string name="month_long_march">mars</string>
+    <string name="month_long_april">avrigl</string>
+    <string name="month_long_may">matg</string>
+    <string name="month_long_june">zercladur</string>
+    <string name="month_long_july">fanadur</string>
+    <string name="month_long_august">avust</string>
+    <string name="month_long_september">settember</string>
+    <string name="month_long_october">october</string>
+    <string name="month_long_november">november</string>
+    <string name="month_long_december">december</string>
+
+    <string name="month_medium_january">schan.</string>
+    <string name="month_medium_february">favr.</string>
+    <string name="month_medium_march">mars</string>
+    <string name="month_medium_april">avr.</string>
+    <string name="month_medium_may">matg</string>
+    <string name="month_medium_june">zercl.</string>
+    <string name="month_medium_july">fan.</string>
+    <string name="month_medium_august">avust</string>
+    <string name="month_medium_september">sett.</string>
+    <string name="month_medium_october">oct.</string>
+    <string name="month_medium_november">nov.</string>
+    <string name="month_medium_december">dec.</string>
+
+    <string name="month_shortest_january">S</string>
+    <string name="month_shortest_february">F</string>
+    <string name="month_shortest_march">M</string>
+    <string name="month_shortest_april">A</string>
+    <string name="month_shortest_may">M</string>
+    <string name="month_shortest_june">Z</string>
+    <string name="month_shortest_july">F</string>
+    <string name="month_shortest_august">A</string>
+    <string name="month_shortest_september">S</string>
+    <string name="month_shortest_october">O</string>
+    <string name="month_shortest_november">N</string>
+    <string name="month_shortest_december">D</string>
+
+    <string name="sunday">dumengia</string>
+    <string name="monday">glindesdi</string>
+    <string name="tuesday">mardi</string>
+    <string name="wednesday">mesemna</string>
+    <string name="thursday">gievgia</string>
+    <string name="friday">venderdi</string>
+    <string name="saturday">sonda</string>
+
+    <string name="day_of_week_long_sunday">dumengia</string>
+    <string name="day_of_week_long_monday">glindesdi</string>
+    <string name="day_of_week_long_tuesday">mardi</string>
+    <string name="day_of_week_long_wednesday">mesemna</string>
+    <string name="day_of_week_long_thursday">gievgia</string>
+    <string name="day_of_week_long_friday">venderdi</string>
+    <string name="day_of_week_long_saturday">sonda</string>
+
+    <string name="day_of_week_medium_sunday">du</string>
+    <string name="day_of_week_medium_monday">gli</string>
+    <string name="day_of_week_medium_tuesday">ma</string>
+    <string name="day_of_week_medium_wednesday">me</string>
+    <string name="day_of_week_medium_thursday">gie</string>
+    <string name="day_of_week_medium_friday">ve</string>
+    <string name="day_of_week_medium_saturday">so</string>
+
+    <string name="day_of_week_short_sunday">du</string>
+    <string name="day_of_week_short_monday">gli</string>
+    <string name="day_of_week_short_tuesday">ma</string>
+    <string name="day_of_week_short_wednesday">me</string>
+    <string name="day_of_week_short_thursday">gie</string>
+    <string name="day_of_week_short_friday">ve</string>
+    <string name="day_of_week_short_saturday">so</string>
+
+    <string name="day_of_week_shorter_sunday">D</string>
+    <string name="day_of_week_shorter_monday">G</string>
+    <string name="day_of_week_shorter_tuesday">M</string>
+    <string name="day_of_week_shorter_wednesday">M</string>
+    <string name="day_of_week_shorter_thursday">G</string>
+    <string name="day_of_week_shorter_friday">V</string>
+    <string name="day_of_week_shorter_saturday">S</string>
+
+    <string name="day_of_week_shortest_sunday">D</string>
+    <string name="day_of_week_shortest_monday">G</string>
+    <string name="day_of_week_shortest_tuesday">M</string>
+    <string name="day_of_week_shortest_wednesday">M</string>
+    <string name="day_of_week_shortest_thursday">G</string>
+    <string name="day_of_week_shortest_friday">V</string>
+    <string name="day_of_week_shortest_saturday">S</string>
+
+    <string name="yesterday">ier</string>
+    <string name="today">oz</string>
+    <string name="tomorrow">damaun</string>
+
+    <string name="hour_minute_24">%-k:%M</string>
+    <string name="hour_minute_ampm">%-l:%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
+    <string name="twelve_hour_time_format">h:mm a</string>
+    <string name="twenty_four_hour_time_format">H:mm</string>
+    <string name="numeric_date">%d.%m.%Y</string>
+    <string name="numeric_date_format">dd.MM.yyyy</string>
+    <string name="numeric_date_template">"%s.%s.%s"</string>
+    <string name="month_day_year">%-e. %B %Y</string>
+    <string name="full_date_month_first">d. MMMM y</string>
+    <string name="full_date_day_first">d. MMMM y</string>
+    <string name="medium_date_month_first">d. MMM y</string>
+    <string name="medium_date_day_first">d. MMM y</string>
+    <string name="time_of_day">%H:%M:%S</string>
+    <string name="status_bar_time_format">HH:mm</string>
+    <string name="date_and_time">%H:%M:%S %d.%m.%Y</string>
+    <string name="date_time">%1$s, %2$s</string>
+    <string name="time_date">%3$s, %1$s</string>
+    <string name="abbrev_month_day_year">%d.%m.%Y</string>
+    <string name="month_day">%-e. %B</string>
+    <string name="month">%-B</string>
+    <string name="month_year">%B %Y</string>
+    <string name="abbrev_month_day">%-e. %b</string>
+    <string name="abbrev_month">%-b</string>
+    <string name="abbrev_month_year">%b %Y</string>
+    <string name="time1_time2">%1$s - %2$s</string>
+    <string name="date1_date2">%2$s - %5$s</string>
+    <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string>
+    <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string>
+    <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s - %10$s %6$s, %9$s-%7$s-%8$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s. - %10$s %8$s.%7$s.</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string>
+    <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %2$s %3$s</string>
+    <string name="wday_date">%2$s %3$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string>
+    <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-xlarge/config.xml
index e92ed11..7e5a27b 100644
--- a/core/res/res/values-xlarge/config.xml
+++ b/core/res/res/values-xlarge/config.xml
@@ -29,5 +29,9 @@
     <bool name="config_enableSlidingTabFirst">false</bool>
     <!-- Enable lockscreen rotation -->
     <bool name="config_enableLockScreenRotation">true</bool>
+
+    <!-- Enables 3d task switcher on xlarge device -->
+    <bool name="config_enableRecentApps3D">true</bool>
+
 </resources>
 
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index 1a16da7..516fb5f 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -29,5 +29,9 @@
     <dimen name="password_keyboard_key_height_alpha">0.35in</dimen>
     <!-- Default height of a key in the password keyboard for numeric -->
     <dimen name="password_keyboard_key_height_numeric">0.47in</dimen>
-</resources>
 
+    <!-- The width that is used when creating thumbnails of applications. -->
+    <dimen name="thumbnail_width">256dp</dimen>
+    <!-- The height that is used when creating thumbnails of applications. -->
+    <dimen name="thumbnail_height">255dp</dimen>
+</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e611b2b..be27528 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -148,6 +148,9 @@
         <!-- Drawable to use for single choice indicators. -->
         <attr name="listChoiceIndicatorSingle" format="reference" />
 
+        <!-- Drawable used as a background for selected list items. -->
+        <attr name="listChoiceBackgroundIndicator" format="reference" />
+
         <!-- ============= -->
         <!-- Button styles -->
         <!-- ============= -->
@@ -1461,7 +1464,7 @@
              If true, touch events may be dispatched to multiple children.
              MotionEvents for each pointer will be dispatched to the child
              view where the initial ACTION_DOWN event happened.
-             See {@link android.view.ViewGroup#setSplitMotionEvents(boolean)}
+             See {@link android.view.ViewGroup#setMotionEventSplittingEnabled(boolean)}
              for more information. -->
         <attr name="splitMotionEvents" format="boolean" />
     </declare-styleable>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index bc130f2..8cf65ae 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -749,15 +749,17 @@
         <attr name="restoreNeedsApplication" />
         <attr name="restoreAnyVersion" />
         <attr name="neverEncrypt" />
-        <!-- Declare that this is a heavy-weight application.  This kind of
-             application is not able to save and restore its state on demand,
-             so can not participate in the normal activity lifecycle.  It will
+        <!-- Declare that this applicationn can't participate in the normal
+             state save/restore mechanism.  Since it is not able to save and
+             restore its state on demand,
+             it can not participate in the normal activity lifecycle.  It will
              not be killed while in the background; the user must explicitly
              quit it.  Only one such app can be running at a time; if the user
-             tries to launch a second heavy-weight app, they will be prompted
-             to quit the first before doing so.  While a heavy-weight
-             application is running, the user will be informed of this. -->
-        <attr name="heavyWeight" format="boolean" />
+             tries to launch a second such app, they will be prompted
+             to quit the first before doing so.  While the
+             application is running, the user will be informed of this.
+             @hide -->
+        <attr name="cantSaveState" format="boolean" />
     </declare-styleable>
     
     <!-- The <code>permission</code> tag declares a security permission that can be
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 30c5184..e9a4ca3 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -37,18 +37,18 @@
     <color name="black">#ff000000</color>
     <color name="transparent">#00000000</color>
     <color name="background_dark">#ff101010</color>
-    <color name="bright_foreground_dark">#ffefefef</color>
+    <color name="background_light">#ffefefef</color>
+    <color name="bright_foreground_dark">@android:color/background_light</color>
+    <color name="bright_foreground_light">@android:color/background_dark</color>
     <color name="bright_foreground_dark_disabled">#80ffffff</color>
-    <color name="bright_foreground_dark_inverse">@android:color/dim_foreground_dark</color>
+    <color name="bright_foreground_light_disabled">#80000000</color>
+    <color name="bright_foreground_dark_inverse">@android:color/bright_foreground_light</color>
+    <color name="bright_foreground_light_inverse">@android:color/bright_foreground_dark</color>
     <color name="dim_foreground_dark">#bebebe</color>
     <color name="dim_foreground_dark_disabled">#80bebebe</color>
     <color name="dim_foreground_dark_inverse">#323232</color>
     <color name="dim_foreground_dark_inverse_disabled">#80323232</color>
     <color name="hint_foreground_dark">#808080</color>
-    <color name="background_light">@android:color/bright_foreground_dark</color>
-    <color name="bright_foreground_light">@android:color/background_dark</color>
-    <color name="bright_foreground_light_inverse">@android:color/bright_foreground_dark</color>
-    <color name="bright_foreground_light_disabled">#80000000</color>
     <color name="dim_foreground_light">#323232</color>
     <color name="dim_foreground_light_disabled">#80323232</color>
     <color name="dim_foreground_light_inverse">#bebebe</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3e3f47c..a071cac 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -299,6 +299,9 @@
     <!-- Diable lockscreen rotation by default -->
     <bool name="config_enableLockScreenRotation">false</bool>
 
+    <!-- Enable 3D RecentApplications view -->
+    <bool name="config_enableRecentApps3D">false</bool>
+
     <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
          The N entries of this array define N + 1 zones as follows:
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ea5c158..353febb 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1250,7 +1250,7 @@
   <eat-comment />
   <public type="attr" name="logo" id="0x010102be" />
   <public type="attr" name="xlargeScreens" id="0x010102bf" />
-  <public type="attr" name="heavyWeight" id="0x010102c0" />
+  <public type="attr" name="cantSaveState" id="0x010102c0" />
   <public type="attr" name="immersive" id="0x010102c1" />
   <public-padding type="attr" name="kraken_resource_pad" end="0x01010300" />
 
@@ -1329,6 +1329,8 @@
   <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. -->
@@ -1347,18 +1349,15 @@
        the base class. -->
   <public type="layout" name="list_content" />
 
-  <!-- A dark holographic theme. -->
   <public type="style" name="Theme.Holo" />
-  <!-- A light holographic theme. -->
   <public type="style" name="Theme.Light.Holo" />
-  <!-- Variant of the holographic (dark) theme with no title bar -->
-  <public type="style" name="Theme.Holo.NoTitleBar" />
-  <!-- Variant of the holographic (dark) theme that has no title bar and fills the entire screen -->
-  <public type="style" name="Theme.Holo.NoTitleBar.Fullscreen" />
-  <!-- Variant of the holographic light theme with no title bar -->
-  <public type="style" name="Theme.Light.Holo.NoTitleBar" />
-  <!-- Variant of the holographic light theme that has no title bar and fills the entire screen -->
-  <public type="style" name="Theme.Light.Holo.NoTitleBar.Fullscreen" />
+  <public type="style" name="Theme.Holo.NoActionBar" />
+  <public type="style" name="Theme.Holo.NoActionBar.Fullscreen" />
+  <public type="style" name="Theme.Light.Holo.NoActionBar" />
+  <public type="style" name="Theme.Light.Holo.NoActionBar.Fullscreen" />
+
+  <!-- A simple ListView item layout which can contain text and support (single or multiple) item selection. -->
+  <public type="layout" name="simple_selectable_list_item" id="0x01090022" />
 
   <public type="string" name="selectTextMode" id="0x01040030" />
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a107048..e38a228 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1887,16 +1887,16 @@
     <!-- Item on EditText context menu. This action is used to copy a URL from the edit field into the clipboard. -->
     <string name="copyUrl">Copy URL</string>
 
-    <!-- Item on EditText context menu. Added only when the context menu is not empty, it enable selection context mode. -->
+    <!-- Item on EditText context menu. Added only when the context menu is not empty, it enable selection context mode. [CHAR LIMIT=20] -->
     <string name="selectTextMode">Select text...</string>
 
-    <!-- Text selection contextual mode title, displayed in the CAB.. -->
+    <!-- Text selection contextual mode title, displayed in the CAB. [CHAR LIMIT=20] -->
     <string name="textSelectionCABTitle">Text selection</string>
 
     <!-- EditText context menu -->
     <string name="inputMethod">Input method</string>
 
-    <!-- Title for EditText context menu -->
+    <!-- Title for EditText context menu [CHAR LIMIT=20] -->
     <string name="editTextMenuTitle">Text actions</string>
 
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the title of that notification. -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 37b66d3..6964808 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -280,22 +280,12 @@
 
     <style name="Widget.CompoundButton.CheckBox">
         <item name="android:background">@android:drawable/btn_check_label_background</item>
-        <item name="android:button">@android:drawable/btn_check</item>
-    </style>
-
-    <style name="Widget.CompoundButton.CheckBox.Inverse">
-        <item name="android:background">@android:drawable/btn_check_label_background_light</item>
-        <item name="android:button">@android:drawable/btn_check_light</item>
+        <item name="android:button">?android:attr/listChoiceIndicatorMultiple</item>
     </style>
 
     <style name="Widget.CompoundButton.RadioButton">
         <item name="android:background">@android:drawable/btn_radio_label_background</item>
-        <item name="android:button">@android:drawable/btn_radio</item>
-    </style>
-
-    <style name="Widget.CompoundButton.RadioButton.Inverse">
-        <item name="android:background">@android:drawable/btn_radio_label_background_light</item>
-        <item name="android:button">@android:drawable/btn_radio_light</item>
+        <item name="android:button">?android:attr/listChoiceIndicatorSingle</item>
     </style>
 
     <style name="Widget.CompoundButton.Star">
@@ -503,7 +493,7 @@
     </style>
     
     <style name="Widget.DropDownItem.Spinner">
-        <item name="android:checkMark">@android:drawable/btn_radio</item>
+        <item name="android:checkMark">?android:attr/listChoiceIndicatorSingle</item>
     </style>
 
     <style name="Widget.ScrollView">
@@ -523,7 +513,7 @@
     </style>
     
     <style name="Widget.ListView.White" parent="Widget.AbsListView">
-        <item name="android:listSelector">@android:drawable/list_selector_background</item>
+        <item name="android:listSelector">@android:drawable/list_selector_background_light</item>
         <item name="android:cacheColorHint">?android:attr/colorBackgroundCacheHint</item>
         <item name="android:divider">@android:drawable/divider_horizontal_bright_opaque</item>
     </style>    
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index c78705b..6ed7b71 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -92,9 +92,11 @@
         <item name="listDivider">@drawable/divider_horizontal_dark</item>
         <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator</item>   
 
-		    <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
+        <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
         <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item>
 
+        <item name="listChoiceBackgroundIndicator">@android:drawable/list_selected_background</item>
+
         <item name="expandableListPreferredItemPaddingLeft">40dip</item>
         <item name="expandableListPreferredChildPaddingLeft">
                 ?android:attr/expandableListPreferredItemPaddingLeft</item>
@@ -168,8 +170,8 @@
         <item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item>
         <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item>
         <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item>
-		    <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
-	      <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item>
+        <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
+        <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item>
         <item name="seekBarStyle">@android:style/Widget.SeekBar</item>
         <item name="ratingBarStyle">@android:style/Widget.RatingBar</item>
         <item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item>
@@ -261,29 +263,25 @@
         <item name="textColorLinkInverse">@android:color/link_text_dark</item>
         
         <item name="editTextColor">?android:attr/textColorPrimary</item>
+        <item name="listChoiceBackgroundIndicator">@android:drawable/list_selected_background_light</item>
+
         <item name="popupWindowStyle">@android:style/Widget.PopupWindow</item>
         
         <item name="textCheckMark">@android:drawable/indicator_check_mark_light</item>
         <item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_dark</item>
 
-        <!-- List attributes -->
-        <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_light</item>
-        <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_light</item>
-
-        <!-- Widget styles -->
-        <item name="checkboxStyle">@android:style/Widget.CompoundButton.CheckBox.Inverse</item>
         <item name="gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView.White</item>
         <item name="expandableListViewStyle">@android:style/Widget.ExpandableListView.White</item>
         <item name="listViewStyle">@android:style/Widget.ListView.White</item>
         <item name="listDivider">@drawable/divider_horizontal_bright</item>
         <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item>
+
         <item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item>
         <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
         <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
         <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
         <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
-		    <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item>
-        <item name="radioButtonStyle">@android:style/Widget.CompoundButton.RadioButton.Inverse</item>
+        <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item>
     </style>
     
     <!-- Variant of the light theme with no title bar -->
@@ -575,35 +573,53 @@
         <item name="android:windowActionModeOverlay">true</item>
     </style>
     
-    <!-- New Honeycomb holographic theme. Dark version -->
+    <!-- New Honeycomb holographic theme. Dark version.  The widgets in the
+         holographic theme are translucent on their brackground, so applications
+         must ensure that any background they use with this theme is itself
+         dark; otherwise, it will be difficult to see the widgets.  The new
+         UI style also includes a full action bar by default. -->
     <style name="Theme.Holo">
         <item name="editTextBackground">@android:drawable/edit_text_holo_dark</item>
         <item name="editTextColor">?android:attr/textColorPrimary</item>
+        <item name="android:windowActionBar">true</item>
+        <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_holo_dark</item>
+        <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_holo_dark</item>
     </style>
 
-    <!-- New Honeycomb holographic theme. Light version -->
+    <!-- New Honeycomb holographic theme. Light version.  The widgets in the
+         holographic theme are translucent on their brackground, so applications
+         must ensure that any background they use with this theme is itself
+         light; otherwise, it will be difficult to see the widgets.  The new
+         UI style also includes a full action bar by default. -->
     <style name="Theme.Light.Holo">
         <item name="editTextBackground">@android:drawable/edit_text_holo_light</item>
+        <item name="android:windowActionBar">true</item>
+        <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_holo_light</item>
+        <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_holo_light</item>
     </style>
 
-    <!-- Variant of the holo (dark) theme with no title bar -->
-    <style name="Theme.Holo.NoTitleBar">
+    <!-- Variant of the holographic (dark) theme with no action bar. -->
+    <style name="Theme.Holo.NoActionBar">
+        <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
     </style>
     
-    <!-- Variant of the holo (dark) theme that has no title bar and fills the entire screen -->
-    <style name="Theme.Holo.NoTitleBar.Fullscreen">
+    <!-- Variant of the holographic (dark) theme that has no title bar and fills
+         the entire screen -->
+    <style name="Theme.Holo.NoActionBar.Fullscreen">
         <item name="android:windowFullscreen">true</item>
         <item name="android:windowContentOverlay">@null</item>
     </style>
 
-    <!-- Variant of the holo light theme with no title bar -->
-    <style name="Theme.Light.Holo.NoTitleBar">
+    <!-- Variant of the holographic light theme with no action bar -->
+    <style name="Theme.Light.Holo.NoActionBar">
+        <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
     </style>
 
-    <!-- Variant of the holo light theme that has no title bar and fills the entire screen -->
-    <style name="Theme.Light.Holo.NoTitleBar.Fullscreen">
+    <!-- Variant of the holographic light theme that has no title bar and fills
+         the entire screen -->
+    <style name="Theme.Light.Holo.NoActionBar.Fullscreen">
         <item name="android:windowFullscreen">true</item>
         <item name="android:windowContentOverlay">@null</item>
     </style>
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index dc5613e..86eda71 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -19,8 +19,11 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
+import android.database.DatabaseErrorHandler;
 import android.database.DatabaseUtils;
+import android.database.DefaultDatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.database.sqlite.SQLiteStatement;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -41,6 +44,7 @@
     private static final int INSERT = 1;
     private static final int UPDATE = 2;
     private static final int DELETE = 3;
+    private static final String DB_NAME = "database_test.db";
 
     @Override
     protected void setUp() throws Exception {
@@ -61,7 +65,7 @@
 
     private void dbSetUp() throws Exception {
         File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
-        mDatabaseFile = new File(dbDir, "database_test.db");
+        mDatabaseFile = new File(dbDir, DB_NAME);
         if (mDatabaseFile.exists()) {
             mDatabaseFile.delete();
         }
@@ -860,7 +864,7 @@
                     "select count(*) from " + TEST_TABLE, null));
             // query in a different thread. but since the transaction is started using
             // execSQ() instead of beginTransaction(), cursor's query is considered part of
-            // the same ransaction - and hence it should see the above inserted row
+            // the same transaction - and hence it should see the above inserted row
             Thread t = new Thread() {
                 @Override public void run() {
                     c1.requery();
@@ -878,11 +882,11 @@
 
     /**
      * This test is same as {@link #testTransactionAndWalInterplay2()} except the following:
-     * instead of commiting the data, do rollback and make sure the data seen by the query
+     * instead of committing the data, do rollback and make sure the data seen by the query
      * within the transaction is now gone.
      */
     @SmallTest
-    public void testTransactionAndWalInterplay3() throws InterruptedException {
+    public void testTransactionAndWalInterplay3() {
         createTableAndClearCache();
         mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
         String sql = "select * from " + TEST_TABLE;
@@ -909,4 +913,36 @@
                 "select count(*) from " + TEST_TABLE, null));
         c.close();
     }
+
+    /**
+     * http://b/issue?id=2943028
+     * SQLiteOpenHelper maintains a Singleton even if it is in bad state.
+     */
+    @SmallTest
+    public void testCloseAndReopen() {
+        mDatabase.close();
+        TestOpenHelper helper = new TestOpenHelper(getContext(), DB_NAME, null,
+                CURRENT_DATABASE_VERSION, new DefaultDatabaseErrorHandler());
+        mDatabase = helper.getWritableDatabase();
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        Cursor c = mDatabase.query(TEST_TABLE, new String[]{"i", "j"}, null, null, null, null, null);
+        assertEquals(1, c.getCount());
+        c.close();
+        mDatabase.close();
+        assertFalse(mDatabase.isOpen());
+        mDatabase = helper.getReadableDatabase();
+        assertTrue(mDatabase.isOpen());
+        c = mDatabase.query(TEST_TABLE, new String[]{"i", "j"}, null, null, null, null, null);
+        assertEquals(1, c.getCount());
+        c.close();
+    }
+    private class TestOpenHelper extends SQLiteOpenHelper {
+        public TestOpenHelper(Context context, String name, CursorFactory factory, int version,
+                DatabaseErrorHandler errorHandler) {
+            super(context, name, factory, version, errorHandler);
+        }
+        @Override public void onCreate(SQLiteDatabase db) {}
+        @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
+    }
 }
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index a5fda20..fe608b5 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -24,6 +24,9 @@
 
 import java.io.File;
 import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 
 public class UriTest extends TestCase {
 
@@ -52,20 +55,20 @@
 
     private void parcelAndUnparcel(Uri u) {
         Parcel p = Parcel.obtain();
-	try {
-		Uri.writeToParcel(p, u);
-		p.setDataPosition(0);
-		assertEquals(u, Uri.CREATOR.createFromParcel(p));
+        try {
+            Uri.writeToParcel(p, u);
+            p.setDataPosition(0);
+            assertEquals(u, Uri.CREATOR.createFromParcel(p));
 
-		p.setDataPosition(0);
-		u = u.buildUpon().build();        
-		Uri.writeToParcel(p, u);
-		p.setDataPosition(0);
-		assertEquals(u, Uri.CREATOR.createFromParcel(p));
-	}
-	finally {
-		p.recycle();
-	}
+            p.setDataPosition(0);
+            u = u.buildUpon().build();
+            Uri.writeToParcel(p, u);
+            p.setDataPosition(0);
+            assertEquals(u, Uri.CREATOR.createFromParcel(p));
+        }
+        finally {
+            p.recycle();
+        }
     }
 
     @SmallTest
@@ -603,4 +606,122 @@
         assertEquals("", uri.getQueryParameter("b"));
         assertEquals("", uri.getQueryParameter("c"));
     }
+
+    public void testGetQueryParameterEmptyKey() {
+        Uri uri = Uri.parse("http://www.google.com/?=b");
+        assertEquals("b", uri.getQueryParameter(""));
+    }
+
+    public void testGetQueryParameterEmptyKey2() {
+      Uri uri = Uri.parse("http://www.google.com/?a=b&&c=d");
+      assertEquals("", uri.getQueryParameter(""));
+    }
+
+    public void testGetQueryParameterEmptyKey3() {
+      Uri uri = Uri.parse("http://www.google.com?");
+      assertEquals("", uri.getQueryParameter(""));
+    }
+
+    public void testGetQueryParameterEmptyKey4() {
+      Uri uri = Uri.parse("http://www.google.com?a=b&");
+      assertEquals("", uri.getQueryParameter(""));
+    }
+
+    public void testGetQueryParametersEmptyKey() {
+        Uri uri = Uri.parse("http://www.google.com/?=b&");
+        List<String> values = uri.getQueryParameters("");
+        assertEquals(2, values.size());
+        assertEquals("b", values.get(0));
+        assertEquals("", values.get(1));
+    }
+
+    public void testGetQueryParametersEmptyKey2() {
+        Uri uri = Uri.parse("http://www.google.com?");
+        List<String> values = uri.getQueryParameters("");
+        assertEquals(1, values.size());
+        assertEquals("", values.get(0));
+    }
+
+    public void testGetQueryParametersEmptyKey3() {
+      Uri uri = Uri.parse("http://www.google.com/?a=b&&c=d");
+      List<String> values = uri.getQueryParameters("");
+      assertEquals(1, values.size());
+      assertEquals("", values.get(0));
+    }
+
+    public void testGetQueryParameterNames() {
+        Uri uri = Uri.parse("http://test?a=1");
+        Set<String> names = uri.getQueryParameterNames();
+        assertEquals(1, names.size());
+        assertEquals("a", names.iterator().next());
+    }
+
+    public void testGetQueryParameterNamesEmptyKey() {
+        Uri uri = Uri.parse("http://www.google.com/?a=x&&c=z");
+        Set<String> names = uri.getQueryParameterNames();
+        Iterator<String> iter = names.iterator();
+        assertEquals(3, names.size());
+        assertEquals("a", iter.next());
+        assertEquals("", iter.next());
+        assertEquals("c", iter.next());
+    }
+
+    public void testGetQueryParameterNamesEmptyKey2() {
+        Uri uri = Uri.parse("http://www.google.com/?a=x&=d&c=z");
+        Set<String> names = uri.getQueryParameterNames();
+        Iterator<String> iter = names.iterator();
+        assertEquals(3, names.size());
+        assertEquals("a", iter.next());
+        assertEquals("", iter.next());
+        assertEquals("c", iter.next());
+    }
+
+    public void testGetQueryParameterNamesEmptyValues() {
+        Uri uri = Uri.parse("http://www.google.com/?a=foo&b=&c=");
+        Set<String> names = uri.getQueryParameterNames();
+        Iterator<String> iter = names.iterator();
+        assertEquals(3, names.size());
+        assertEquals("a", iter.next());
+        assertEquals("b", iter.next());
+        assertEquals("c", iter.next());
+    }
+
+    public void testGetQueryParameterNamesEdgeCases() {
+        Uri uri = Uri.parse("http://foo?a=bar&b=bar&c=&&d=baz&e&f&g=buzz&&&a&b=bar&h");
+        Set<String> names = uri.getQueryParameterNames();
+        Iterator<String> iter = names.iterator();
+        assertEquals(9, names.size());
+        assertEquals("a", iter.next());
+        assertEquals("b", iter.next());
+        assertEquals("c", iter.next());
+        assertEquals("", iter.next());
+        assertEquals("d", iter.next());
+        assertEquals("e", iter.next());
+        assertEquals("f", iter.next());
+        assertEquals("g", iter.next());
+        assertEquals("h", iter.next());
+    }
+
+    public void testGetQueryParameterNamesEscapedKeys() {
+        Uri uri = Uri.parse("http://www.google.com/?a%20b=foo&c%20d=");
+        Set<String> names = uri.getQueryParameterNames();
+        assertEquals(2, names.size());
+        Iterator<String> iter = names.iterator();
+        assertEquals("a b", iter.next());
+        assertEquals("c d", iter.next());
+    }
+
+    public void testGetQueryParameterEscapedKeys() {
+        Uri uri = Uri.parse("http://www.google.com/?a%20b=foo&c%20d=");
+        String value = uri.getQueryParameter("a b");
+        assertEquals("foo", value);
+    }
+    
+    public void testClearQueryParameters() {
+        Uri uri = Uri.parse("http://www.google.com/?a=x&b=y&c=z").buildUpon()
+            .clearQuery().appendQueryParameter("foo", "bar").build();
+        Set<String> names = uri.getQueryParameterNames();
+        assertEquals(1, names.size());
+        assertEquals("foo", names.iterator().next());
+    }
 }
diff --git a/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java b/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java
index f3b7c06..95aad91 100644
--- a/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java
+++ b/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java
@@ -22,8 +22,8 @@
 import java.io.StringWriter;
 import java.util.Arrays;
 import java.util.List;
-import javax.net.ssl.TestSSLContext;
 import junit.framework.TestCase;
+import libcore.javax.net.ssl.TestSSLContext;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index b7c2d1f..f82bfce 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -41,6 +41,10 @@
         }
 
         public void handleMessage(Message msg) {
+            if (!msg.isInUse()) {
+                failure(new RuntimeException(
+                        "msg.isInuse is false, should always be true, #" + msg.what));
+            }
             if (mCount <= mLastMessage) {
                 if (msg.what != mCount) {
                     failure(new RuntimeException(
@@ -99,5 +103,174 @@
 
         tester.doTest(1000);
     }
-}
 
+    private static class TestFieldIntegrityHandler extends TestHandlerThread {
+        Handler mHandler;
+        int mLastMessage;
+        int mCount;
+
+        public TestFieldIntegrityHandler() {
+        }
+
+        public void go() {
+            mHandler = new Handler() {
+                public void handleMessage(Message msg) {
+                    TestFieldIntegrityHandler.this.handleMessage(msg);
+                }
+            };
+        }
+
+        public void handleMessage(Message msg) {
+            if (!msg.isInUse()) {
+                failure(new RuntimeException(
+                        "msg.isInuse is false, should always be true, #" + msg.what));
+            }
+            if (mCount <= mLastMessage) {
+                if (msg.what != mCount) {
+                    failure(new RuntimeException(
+                            "Expected message #" + mCount
+                                    + ", received #" + msg.what));
+                } else if (mCount == mLastMessage) {
+                    success();
+                }
+                mCount++;
+            } else {
+                failure(new RuntimeException(
+                        "Message received after done, #" + msg.what));
+            }
+        }
+    }
+
+    @MediumTest
+    public void testFieldIntegrity() throws Exception {
+
+        TestHandlerThread tester = new TestFieldIntegrityHandler() {
+            Bundle mBundle;
+
+            public void go() {
+                super.go();
+                mLastMessage = 1;
+                mCount = 0;
+                mHandler.sendMessage(mHandler.obtainMessage(0));
+            }
+
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+                if (msg.what == 0) {
+                    msg.flags = -1;
+                    msg.what = 1;
+                    msg.arg1 = 456;
+                    msg.arg2 = 789;
+                    msg.obj = this;
+                    msg.replyTo = null;
+                    mBundle = new Bundle();
+                    msg.data = mBundle;
+                    msg.data.putString("key", "value");
+
+                    Message newMsg = mHandler.obtainMessage();
+                    newMsg.copyFrom(msg);
+                    if (newMsg.isInUse() != false) {
+                        failure(new RuntimeException(
+                                "newMsg.isInUse is true should be false after copyFrom"));
+                    }
+                    if (newMsg.flags != 0) {
+                        failure(new RuntimeException(String.format(
+                        "newMsg.flags is %d should be 0 after copyFrom", newMsg.flags)));
+                    }
+                    if (newMsg.what != 1) {
+                        failure(new RuntimeException(String.format(
+                                "newMsg.what is %d should be %d after copyFrom", newMsg.what, 1)));
+                    }
+                    if (newMsg.arg1 != 456) {
+                        failure(new RuntimeException(String.format(
+                                "newMsg.arg1 is %d should be %d after copyFrom", msg.arg1, 456)));
+                    }
+                    if (newMsg.arg2 != 789) {
+                        failure(new RuntimeException(String.format(
+                                "newMsg.arg2 is %d should be %d after copyFrom", msg.arg2, 789)));
+                    }
+                    if (newMsg.obj != this) {
+                        failure(new RuntimeException(
+                                "newMsg.obj should be 'this' after copyFrom"));
+                    }
+                    if (newMsg.replyTo != null) {
+                        failure(new RuntimeException(
+                                "newMsg.replyTo should be null after copyFrom"));
+                    }
+                    if (newMsg.data == mBundle) {
+                        failure(new RuntimeException(
+                                "newMsg.data should NOT be mBundle after copyFrom"));
+                    }
+                    if (!newMsg.data.getString("key").equals(mBundle.getString("key"))) {
+                        failure(new RuntimeException(String.format(
+                                "newMsg.data.getString(\"key\") is %s and does not equal" +
+                                " mBundle.getString(\"key\") which is %s after copyFrom",
+                                newMsg.data.getString("key"),  mBundle.getString("key"))));
+                    }
+                    if (newMsg.when != 0) {
+                        failure(new RuntimeException(String.format(
+                                "newMsg.when is %d should be 0 after copyFrom", newMsg.when)));
+                    }
+                    if (newMsg.target != mHandler) {
+                        failure(new RuntimeException(
+                                "newMsg.target is NOT mHandler after copyFrom"));
+                    }
+                    if (newMsg.callback != null) {
+                        failure(new RuntimeException(
+                                "newMsg.callback is NOT null after copyFrom"));
+                    }
+
+                    mHandler.sendMessage(newMsg);
+                } else if (msg.what == 1) {
+                    if (msg.isInUse() != true) {
+                        failure(new RuntimeException(String.format(
+                                "msg.isInUse is false should be true after when processing %d",
+                                msg.what)));
+                    }
+                    if (msg.arg1 != 456) {
+                        failure(new RuntimeException(String.format(
+                                "msg.arg1 is %d should be %d when processing # %d",
+                                msg.arg1, 456, msg.what)));
+                    }
+                    if (msg.arg2 != 789) {
+                        failure(new RuntimeException(String.format(
+                                "msg.arg2 is %d should be %d when processing # %d",
+                                msg.arg2, 789, msg.what)));
+                    }
+                    if (msg.obj != this) {
+                        failure(new RuntimeException(String.format(
+                                "msg.obj should be 'this' when processing # %d", msg.what)));
+                    }
+                    if (msg.replyTo != null) {
+                        failure(new RuntimeException(String.format(
+                                "msg.replyTo should be null when processing # %d", msg.what)));
+                    }
+                    if (!msg.data.getString("key").equals(mBundle.getString("key"))) {
+                        failure(new RuntimeException(String.format(
+                                "msg.data.getString(\"key\") is %s and does not equal" +
+                                " mBundle.getString(\"key\") which is %s when processing # %d",
+                                msg.data.getString("key"),  mBundle.getString("key"), msg.what)));
+                    }
+                    if (msg.when != 0) {
+                        failure(new RuntimeException(String.format(
+                                "msg.when is %d should be 0 when processing # %d",
+                                msg.when, msg.what)));
+                    }
+                    if (msg.target != null) {
+                        failure(new RuntimeException(String.format(
+                                "msg.target is NOT null when processing # %d", msg.what)));
+                    }
+                    if (msg.callback != null) {
+                        failure(new RuntimeException(String.format(
+                                "msg.callback is NOT null when processing # %d", msg.what)));
+                    }
+                } else {
+                    failure(new RuntimeException(String.format(
+                            "Unexpected msg.what is %d" + msg.what)));
+                }
+            }
+        };
+
+        tester.doTest(1000);
+    }
+}
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index d0318cf..35ce17e 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -220,9 +220,16 @@
             <li><a style="color:gray;">Accelerometer</a></li>
           </ul>
       </li> -->
-      <li><a href="<?cs var:toroot ?>guide/topics/location/index.html">
-            <span class="en">Location and Maps</span>
-          </a></li>
+      <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>guide/topics/location/index.html">
+               <span class="en">Location and Maps</span>
+             </a></div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>guide/topics/location/obtaining-user-location.html">
+                <span class="en">Obtaining User Location</span>
+              </a> <span class="new">new!</span></li>
+        </ul>
+      </li>
   <!--<li class="toggle-list">
         <div><a style="color:gray;">Wireless Controls</a></div>
           <ul>
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index f780e7c..db06efc 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -292,8 +292,8 @@
 <li><p>A service is started (or new instructions are given to an ongoing 
 service) by passing an Intent object to <code>{@link 
 android.content.Context#startService Context.startService()}</code>.  
-Android calls the service's <code>{@link android.app.Service#onStart 
-onStart()}</code> method and passes it the Intent object.</p>
+Android calls the service's <code>{@link android.app.Service#onStartCommand
+onStartCommand()}</code> method and passes it the Intent object.</p>
 
 <p>
 Similarly, an intent can be passed to <code>{@link 
@@ -770,9 +770,9 @@
 </p>
 
 <p>
-For more on launch modes, see the description of the 
-<code><a href="{@docRoot}guide/topics/manifest/activity-element.html">&lt;activity&gt;</a></code>
-element. 
+For more on launch modes, see the description of the <code><a 
+href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">&lt;activity&gt;</a></code>
+element.
 </p>
 
 
@@ -1508,9 +1508,9 @@
 in {@code onCreate()}, and then stop the thread in {@code onDestroy()}.</li>
 
 <li><p>The <b>active lifetime</b> of a service begins with a call to 
-<code>{@link android.app.Service#onStart onStart()}</code>.  This method 
+<code>{@link android.app.Service#onStartCommand onStartCommand()}</code>.  This method
 is handed the Intent object that was passed to {@code startService()}.
-The music service would open the Intent to discover which music to 
+The music service would open the Intent to discover which music to
 play, and begin the playback.</p>
 
 <p>
@@ -1525,7 +1525,7 @@
 <code>{@link android.content.Context#startService Context.startService()}</code>
 or 
 <code>{@link android.content.Context#bindService Context.bindService()}</code>.
-However, {@code onStart()} is called only for services started by {@code
+However, {@code onStartCommand()} is called only for services started by {@code
 startService()}.
 </p>
 
@@ -1629,7 +1629,7 @@
 
 <li><p>It has a {@link android.app.Service} object that's executing
 one of its lifecycle callbacks (<code>{@link android.app.Service#onCreate 
-onCreate()}</code>, <code>{@link android.app.Service#onStart onStart()}</code>, 
+onCreate()}</code>, <code>{@link android.app.Service#onStartCommand onStartCommand()}</code>,
 or <code>{@link android.app.Service#onDestroy onDestroy()}</code>).</p></li>
 
 <li><p>It has a {@link android.content.BroadcastReceiver} object that's 
diff --git a/docs/html/guide/topics/location/index.jd b/docs/html/guide/topics/location/index.jd
index e988ecb..5f98902 100644
--- a/docs/html/guide/topics/location/index.jd
+++ b/docs/html/guide/topics/location/index.jd
@@ -4,94 +4,63 @@
 <div id="qv-wrapper">
 <div id="qv">
 
-  <h2>Location and Maps quickview</h2>
+  <h2>Quickview</h2>
   <ul>
-    <li>Android provides a location framework that your application can use to determine the device's location and bearing and register for updates.</li>
-    <li>A Google Maps external library is available that lets you display and manage Maps data.</li>
+    <li>Android provides a location framework that your application can use to determine the
+device's location and bearing and register for updates</li>
+    <li>A Google Maps external library is available that lets you display and manage Maps data</li>
   </ul>
-  <h2>In this document</h2>
+
+  <h2>Topics</h2>
   <ol>
-    <li><a href="#location">Location Services</a></li>
-    <li><a href="#maps">Google Maps External Library</a></li>
+    <li><a href="{@docRoot}guide/topics/location/obtaining-user-location.html">Obtaining User
+Location</a></li>
   </ol>
+  
   <h2>See Also</h2>
   <ol>
-    <li><a href="http://code.google.com/android/add-ons/google-apis/index.html">Google APIs add-on download&raquo;</a></li>
+    <li><a
+href="http://code.google.com/android/add-ons/google-apis/maps-overview.html">Google
+Maps External Library &raquo;</a></li>
   </ol>
 </div>
 </div>
 
-<p>Location- and maps-based applications and services are compelling for mobile device users. You can build these capabilities into your applications using the classes of the {@link android.location} package and the Google Maps external library. The sections below provide details. </p>
+<p>Location and maps-based applications are compelling for mobile device users. You
+can build these capabilities into your applications using the classes of the {@link
+android.location} package and the Google Maps external library. The sections below provide details.
+</p>
 
 <h2 id="location">Location Services</h2>
 
 <p>Android gives your applications access to the location services supported by
-the device through the classes in the <code>android.location</code> package. The
+the device through the classes in the {@code android.location} package. The
 central component of the location framework is the 
-{@link android.location.LocationManager} system service, which provides an API to
-determine location and bearing if the underlying device (if it supports location
-capabilities). </p>
+{@link android.location.LocationManager} system service, which provides APIs to
+determine location and bearing of the underlying device (if available). </p>
 
-<p>As with other system services, you do not instantiate a LocationManager directly. 
-Rather, you request an LocationManager instance from the system by calling 
-{@link android.content.Context#getSystemService(String) getSystemService(Context.LOCATION_SERVICE)}. 
-The method returns a handle to a new LocationManager instance.</p>
+<p>As with other system services, you do not instantiate a {@link android.location.LocationManager}
+directly. Rather, you request an instance from the system by calling
+{@link android.content.Context#getSystemService(String)
+getSystemService(Context.LOCATION_SERVICE)}. The method returns a handle to a new {@link
+android.location.LocationManager} instance.</p>
 
-<p>Once your application has a handle to a LocationManager instance, your application 
-will be able to do three things:</p>
+<p>Once your application has a {@link android.location.LocationManager}, your application
+is able to do three things:</p>
 
 <ul>
-    <li>Query for the list of all LocationProviders known to the
-    LocationManager for its last known location.</li>
-    <li>Register/unregister for periodic updates of current location from a
-    LocationProvider (specified either by Criteria or name).</li>
-    <li>Register/unregister for a given Intent to be fired if the device comes
-    within a given proximity (specified by radius in meters) of a given
-    lat/long.</li>
+    <li>Query for the list of all {@link android.location.LocationProvider}s for the last known
+user location.</li>
+    <li>Register/unregister for periodic updates of the user's current location from a
+    location provider (specified either by criteria or name).</li>
+    <li>Register/unregister for a given {@link android.content.Intent} to be fired if the device
+comes within a given proximity (specified by radius in meters) of a given lat/long.</li>
 </ul>
 
-<p>However, during initial development in the emulator, you may not have access to real 
-data from a real location provider (Network or GPS). In that case, it may be necessary to
-spoof some data for your application using a mock location provider.</p>
+<p>For more information, read the guide to <a
+href="{@docRoot}guide/topics/location/obtaining-user-location.html">Obtaining User
+Location</a>.</p>
 
-<p class="note"><strong>Note:</strong> If you've used mock LocationProviders in
-previous versions of the SDK, you can no longer provide canned LocationProviders
-in the /system/etc/location directory. These directories will be wiped during boot-up.
-Please follow the new procedures outlined below.</p>
-
-<h3>Providing Mock Location Data</h3>
-
-<p>When testing your application on the Android emulator, there are a couple different
-ways to send it some mock location data: you can use the DDMS tool or the "geo" command 
-option in the emulator console.</p>
-
-<h4 id="ddms">Using DDMS</h4>
-<p>With the DDMS tool, you can simulate location data a few different ways:</p>
-<ul>
-    <li>Manually send individual longitude/latitude coordinates to the device.</li>
-    <li>Use a GPX file describing a route for playback to the device.</li>
-    <li>Use a KML file describing individual placemarks for sequenced playback to the device.</li>
-</ul>
-<p>For more information on using DDMS to spoof location data, see the 
-<a href="{@docRoot}guide/developing/tools/ddms.html#emulator-control">Using DDMS guide</a>.
-
-<h4 id="geo">Using the "geo" command in the emulator console</h4>
-<p>Launch your application in the Android emulator and open a terminal/console in
-your SDK's <code>/tools</code> directory. Connect to the emulator console. Now you can use:</p>
-<ul><li><code>geo fix</code> to send a fixed geo-location.
-	<p>This command accepts a longitude and latitude in decimal degrees, and
-	an optional altitude in meters. For example:</p>
-	<pre>geo fix -121.45356 46.51119 4392</pre>
-    </li>
-    <li><code>geo nmea</code> to send an NMEA 0183 sentence.
-	<p>This command accepts a single NMEA sentence of type '$GPGGA' (fix data) or '$GPRMC' (transit data).
-	For example:</p>
-	<pre>geo nmea $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62</pre>
-    </li>
-</ul>
-
-<p>For information about how to connect to the emulator console, see 
-<a href="{@docRoot}guide/developing/tools/emulator.html#console">Using the Emulator Console</a>.</p>
 
 <h2 id="maps">Google Maps External Library</h2>
 
@@ -128,9 +97,9 @@
 <p style="margin-left:2em;"><a
 href="http://code.google.com/android/add-ons/google-apis">http://code.google.com/android/add-ons/google-apis</a></p>
 
-<p>For your convenience, the Google APIs add-on is also included in the Android
-SDK. <!-- To learn now to use the Maps external library in your application, see
-[[Using External Libraries]].--></p>
+<p>For your convenience, the Google APIs add-on is also available as a downloadable component from
+the Android SDK and AVD Manager (see <a href="{@docRoot}sdk/adding-components.html">Adding SDK
+Components</a>).</p>
 
 <p class="note"><strong>Note:</strong> In order to display Google Maps data in a
 MapView, you must register with the Google Maps service and obtain a Maps API
diff --git a/docs/html/guide/topics/location/obtaining-user-location.jd b/docs/html/guide/topics/location/obtaining-user-location.jd
new file mode 100644
index 0000000..bc782d2
--- /dev/null
+++ b/docs/html/guide/topics/location/obtaining-user-location.jd
@@ -0,0 +1,454 @@
+page.title=Obtaining User Location
+parent.title=Location and Maps
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+  <h2>Quickview</h2>
+  <ul>
+    <li>The Network Location Provider provides good location data without using GPS</li>
+    <li>Obtaining user location can consume a lot of battery, so be careful how
+long you listen for updates</li>
+  </ul>
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#Challenges">Challenges in Determining User Location</a></li>
+    <li><a href="#Updates">Requesting Location Updates</a>
+      <ol>
+        <li><a href="#Permission">Requesting User Permissions</a></li>
+      </ol>
+    </li>
+    <li><a href="#BestPerformance">Defining a Model for the Best Performance</a>
+      <ol>
+        <li><a href="#Flow">Flow for obtaining user location</a></li>
+        <li><a href="#StartListening">Deciding when to start listening for updates</a></li>
+        <li><a href="#FastFix">Getting a fast fix with the last known location</a></li>
+        <li><a href="#StopListening">Deciding when to stop listening for updates</a></li>
+        <li><a href="#BestEstimate">Maintaining a current best estimate</a></li>
+        <li><a href="#Adjusting">Adjusting the model to save battery and data exchange</a></li>
+      </ol>
+    </li>
+    <li><a href="#MockData">Providing Mock Location Data</a></li>
+  </ol>
+  <h2>Key classes</h2>
+  <ol>
+    <li>{@link android.location.LocationManager}</li>
+    <li>{@link android.location.LocationListener}</li>
+  </ol>
+</div>
+</div>
+
+  <p>Knowing where the user is allows your application to be smarter and deliver
+better information to the user. When developing a location-aware application for Android, you can
+utilize GPS and Android's Network Location Provider to acquire the user location. Although
+GPS is most accurate, it only works outdoors, it quickly consumes battery power, and doesn't return
+the location as quickly as users want. Android's Network Location Provider determines user location
+using cell tower and Wi-Fi signals, providing location information in a way that
+works indoors and outdoors, responds faster, and uses less battery power. To obtain the user
+location in your application, you can use both GPS and the Network Location Provider, or just
+one.</p>
+
+
+<h2 id="Challenges">Challenges in Determining User Location</h2>
+
+<p>Obtaining user location from a mobile device can be complicated. There are several reasons
+why a location reading (regardless of the source) can contain errors and be inaccurate.
+Some sources of error in the user location include:</p>
+
+<ul>
+  <li><b>Multitude of location sources</b>
+    <p>GPS, Cell-ID, and Wi-Fi can each provide a clue to users location. Determining which to use
+and trust is a matter of trade-offs in accuracy, speed, and battery-efficiency.</p>
+  </li>
+  <li><b>User movement</b>
+    <p>Because the user location changes, you must account for movement by re-estimating user
+location every so often.</p>
+  </li>
+  <li><b>Varying accuracy</b>
+    <p>Location estimates coming from each location source are not consistent in their
+accuracy. A location obtained 10 seconds ago from one source might be more accurate than the newest
+location from another or same source.</p>
+  </li>
+</ul>
+
+  <p>These problems can make it difficult to obtain a reliable user location reading. This
+document provides information to help you meet these challenges to obtain a reliable location
+reading. It also provides ideas that you can use in your
+application to provide the user with an accurate and responsive geo-location experience.</p>
+
+
+<h2 id="Updates">Requesting Location Updates</h2>
+
+  <p>Before addressing some of the location errors described above, here is an introduction to
+how you can obtain user location on Android.</p>
+
+  <p>Getting user location in Android works by means of callback. You indicate that you'd
+like to receive location updates from the {@link android.location.LocationManager} ("Location
+Manager") by calling {@link android.location.LocationManager#requestLocationUpdates
+requestLocationUpdates()}, passing it a
+{@link android.location.LocationListener}. Your {@link android.location.LocationListener} must
+implement several callback methods that the Location Manager calls when the user location
+changes or when the status of the service changes.</p>
+
+<p>For example, the following code shows how to define a {@link android.location.LocationListener}
+and request location updates:
+  </p>
+
+<pre>
+// Acquire a reference to the system Location Manager
+LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+
+// Define a listener that responds to location updates
+LocationListener locationListener = new LocationListener() {
+    public void onLocationChanged(Location location) {
+      // Called when a new location is found by the network location provider.
+      makeUseOfNewLocation(location);
+    }
+
+    public void onStatusChanged(String provider, int status, Bundle extras) {}
+
+    public void onProviderEnabled(String provider) {}
+
+    public void onProviderDisabled(String provider) {}
+  };
+
+// Register the listener with the Location Manager to receive location updates
+locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
+</pre>
+
+  <p>The first parameter in {@link
+android.location.LocationManager#requestLocationUpdates requestLocationUpdates()} is the type of
+location provider to use (in this case, the Network Location Provider for cell tower and Wi-Fi
+based location). You can control the frequency at which your listener receives updates
+with the second and third parameter&mdash;the second is the minimum time interval between
+notifications and the third is the minimum change in distance between notifications&mdash;setting
+both to zero requests location notifications as frequently as possible. The last parameter is your
+{@link android.location.LocationListener}, which receives callbacks for location updates.</p>
+
+<p>To request location updates from the GPS provider,
+substitute <code>GPS_PROVIDER</code> for <code>NETWORK_PROVIDER</code>. You can also request
+location updates from both the GPS and the Network Location Provider by calling {@link
+android.location.LocationManager#requestLocationUpdates requestLocationUpdates()} twice&mdash;once
+for <code>NETWORK_PROVIDER</code> and once for <code>GPS_PROVIDER</code>.</p>
+
+
+<h3 id="Permission">Requesting User Permissions</h3>
+
+<p>In order to receive location updates from <code>NETWORK_PROVIDER</code> or
+<code>GPS_PROVIDER</code>, you must request user permission by declaring either the {@code
+ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION} permission, respectively, in your Android
+manifest file. For example:</p>
+
+<pre>
+&lt;manifest ... &gt;
+    &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /&gt;
+    ...
+&lt;/manifest&gt;
+</pre>
+
+<p>Without these permissions, your application will fail at runtime when requesting
+location updates.</p>
+
+<p class="note"><strong>Note:</strong> If you are using both <code>NETWORK_PROVIDER</code> and
+<code>GPS_PROVIDER</code>, then you need to request only the {@code ACCESS_FINE_LOCATION}
+permission, because it includes permission for both providers. (Permission for {@code
+ACCESS_COARSE_LOCATION} includes permission only for <code>NETWORK_PROVIDER</code>.)</p>
+
+
+<h2 id="BestPerformance">Defining a Model for the Best Performance</h2>
+
+  <p>Location-based applications are now commonplace, but due to the less than optimal
+accuracy, user movement, the multitude of methods to obtain the location, and the desire to conserve
+battery, getting user location is complicated. To overcome the obstacles of obtaining a good user
+location while preserving battery power, you must define a consistent model that specifies how your
+application obtains the user location. This model includes when you start and stop listening for
+updates and when to use cached location data.</p>
+
+
+  <h3 id="Flow">Flow for obtaining user location</h3>
+
+  <p>Here's the typical flow of procedures for obtaining the user location:</p>
+
+  <ol>
+    <li>Start application.</li>
+    <li>Sometime later, start listening for updates from desired location providers.</li>
+    <li>Maintain a "current best estimate" of location by filtering out new, but less accurate
+fixes.</li>
+    <li>Stop listening for location updates.</li>
+    <li>Take advantage of the last best location estimate.</li>
+  </ol>
+
+  <p>Figure 1 demonstrates this model in a timeline that visualizes the period in which an
+application is listening for location updates and the events that occur during that time.</p>
+
+<img src="{@docRoot}images/location/getting-location.png" alt="" />
+<p class="img-caption"><strong>Figure 1.</strong> A timeline representing the window in which an
+application listens for location updates.</p>
+    
+  <p>This model of a window&mdash;during which location updates are received&mdash;frames many of
+the decisions you need to make when adding location-based services to your application.</p>
+
+
+  <h3 id="StartListening">Deciding when to start listening for updates</h3>
+
+  <p>You might want to start listening for location updates as soon as your application starts, or
+only after users activate a certain feature. Be aware that long windows of listening for location
+fixes can consume a lot of battery power, but short periods might not allow for sufficient
+accuracy.</p>
+
+  <p>As demonstrated above, you can begin listening for updates by calling {@link
+android.location.LocationManager#requestLocationUpdates requestLocationUpdates()}:</p>
+
+<pre>
+LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER;
+// Or, use GPS location data:
+// LocationProvider locationProvider = LocationManager.GPS_PROVIDER;
+
+locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
+</pre>
+
+
+  <h3 id="FastFix">Getting a fast fix with the last known location</h3>
+  
+  <p>The time it takes for your location listener to receive the first location fix is often too
+long for users wait. Until a more accurate location is provided to your location listener, you
+should utilize a cached location by calling {@link
+android.location.LocationManager#getLastKnownLocation}:</p>
+<pre>
+LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER;
+// Or use LocationManager.GPS_PROVIDER
+
+Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
+</pre>
+
+
+  <h3 id="StopListening">Deciding when to stop listening for updates</h3>
+  
+  <p>The logic of deciding when new fixes are no longer necessary might range from very simple to
+very complex depending on your application. A short gap between when the location is acquired and
+when the location is used, improves the accuracy of the estimate. Always beware that listening for a
+long time consumes a lot of battery power, so as soon as you have the information you need, you
+should stop
+listening for updates by calling {@link android.location.LocationManager#removeUpdates}:</p>
+<pre>
+// Remove the listener you previously added
+locationManager.removeUpdates(locationListener);
+</pre>
+
+
+  <h3 id="BestEstimate">Maintaining a current best estimate</h3>
+
+  <p>You might expect that the most recent location fix is the most accurate.
+However, because the accuracy of a location fix varies, the most recent fix is not always the best.
+You should include logic for choosing location fixes based on several criteria. The criteria also
+varies depending on the use-cases of the application and field testing.</p>
+
+  <p>Here are a few steps you can take to validate the accuracy of a location fix:</p>
+  <ul>
+    <li>Check if the location retrieved is significantly newer than the previous estimate.</li>
+    <li>Check if the accuracy claimed by the location is better or worse than the previous
+estimate.</li>
+    <li>Check which provider the new location is from and determine if you trust it more.</li>
+  </ul>
+  
+  <p>An elaborate example of this logic can look something like this:</p>
+
+<pre>
+private static final int TWO_MINUTES = 1000 * 60 * 2;
+
+/** Determines whether one Location reading is better than the current Location fix
+  * @param location  The new Location that you want to evaluate
+  * @param currentBestLocation  The current Location fix, to which you want to compare the new one
+  */
+protected boolean isBetterLocation(Location location, Location currentBestLocation) {
+    if (currentBestLocation == null) {
+        // A new location is always better than no location
+        return true;
+    }
+
+    // Check whether the new location fix is newer or older
+    long timeDelta = location.getTime() - currentBestLocation.getTime();
+    boolean isSignificantlyNewer = timeDelta &gt; TWO_MINUTES;
+    boolean isSignificantlyOlder = timeDelta &lt; -TWO_MINUTES;
+    boolean isNewer = timeDelta > 0;
+
+    // If it's been more than two minutes since the current location, use the new location
+    // because the user has likely moved
+    if (isSignificantlyNewer) {
+        return true;
+    // If the new location is more than two minutes older, it must be worse
+    } else if (isSignificantlyOlder) {
+        return false;
+    }
+
+    // Check whether the new location fix is more or less accurate
+    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
+    boolean isLessAccurate = accuracyDelta &gt; 0;
+    boolean isMoreAccurate = accuracyDelta &lt; 0;
+    boolean isSignificantlyLessAccurate = accuracyDelta &gt; 200;
+
+    // Check if the old and new location are from the same provider
+    boolean isFromSameProvider = isSameProvider(location.getProvider(),
+            currentBestLocation.getProvider());
+
+    // Determine location quality using a combination of timeliness and accuracy
+    if (isMoreAccurate) {
+        return true;
+    } else if (isNewer &amp;&amp; !isLessAccurate) {
+        return true;
+    } else if (isNewer &amp;&amp; !isSignificantlyLessAccurate &amp;&amp; isFromSameProvider) {
+        return true;
+    }
+    return false;
+}
+
+/** Checks whether two providers are the same */
+private boolean isSameProvider(String provider1, String provider2) {
+    if (provider1 == null) {
+      return provider2 == null;
+    }
+    return provider1.equals(provider2);
+}
+</pre>
+
+
+  <h3 id="Adjusting">Adjusting the model to save battery and data exchange</h3>
+
+  <p>As you test your application, you might find that your model for providing good location and
+good performance needs some adjustment. Here are some things you might change to find a good
+balance between the two.</p>
+
+  <h4>Reduce the size of the window</h4>
+  
+  <p>A smaller window in which you listen for location updates means less interaction with GPS and
+network location services, thus, preserving battery life. But it also allows for fewer locations
+from which to choose a best estimate.</p>
+
+  <h4>Set the location providers to return updates less frequently</h4>
+  
+  <p>Reducing the rate at which new updates appear during the window can also improve battery
+efficiency, but at the cost of accuracy. The value of the trade-off depends on how your
+application is used. You can reduce the rate of updates by increasing the parameters in {@link
+android.location.LocationManager#requestLocationUpdates requestLocationUpdates()} that specify the
+interval time and minimum distance change.</p>
+
+  <h4>Restrict a set of providers</h4>
+  
+  <p>Depending on the environment where your application is used or the desired level of accuracy,
+you might choose to use only the Network Location Provider or only GPS, instead of both. Interacting
+with only one of the services reduces battery usage at a potential cost of accuracy.</p>
+
+
+  <h2>Common application cases</h2>
+  
+  <p>There are many reasons you might want to obtain the user location in your application. Below
+are a couple scenarios in which you can use the user location to enrich your application. Each
+scenario also describes good practices for when you should start and stop listening for the
+location, in order to get a good reading and help preserve battery life.</p>
+
+
+  <h3>Tagging user-created content with a location</h3>
+  
+  <p>You might be creating an application where user-created content is tagged with a location.
+Think of users sharing their local experiences, posting a review for a restaurant, or recording some
+content that can be augmented with their current location. A model of how this
+interaction might happen, with respect to the location services, is visualized in figure 2.</p>
+
+  <img src="{@docRoot}images/location/content-tagging.png" alt="" />
+<p class="img-caption"><strong>Figure 2.</strong> A timeline representing the window in which
+the user location is obtained and listening stops when the user consumes the current location.</p>
+  
+  <p>This lines up with the previous model of how user location is obtained in code (figure 1). For
+best location accuracy, you might choose to start listening for location updates when users begin
+creating
+the content or even when the application starts, then stop listening for updates when content is
+ready to be posted or recorded. You might need to consider how long a typical task of creating the
+content takes and judge if this duration allows for efficient collection of a location estimate.</p>
+
+
+  <h3>Helping the user decide on where to go</h3>
+  
+  <p>You might be creating an application that attempts to provide users with a set
+of options about where to go. For example, you're looking to provide a list of nearby restaurants,
+stores, and entertainment and the order of recommendations changes depending on the user
+location.</p>
+
+  <p>To accommodate such a flow, you might choose to:</p>
+  <ul>
+    <li>Rearrange recommendations when a new best estimate is obtained</li>
+    <li>Stop listening for updates if the order of recommendations has stabilized</li>
+  </ul>
+
+  <p>This kind of model is visualized in figure 3.</p>
+  
+  <img src="{@docRoot}images/location/where-to-go.png" alt="" />
+<p class="img-caption"><strong>Figure 3.</strong> A timeline representing the window in which a
+dynamic set of data is updated each time the user location updates.</p>
+
+
+
+
+<h2 id="MockData">Providing Mock Location Data</h2>
+
+<p>As you develop your application, you'll certainly need to test how well your model for obtaining
+user location works. This is most easily done using a real Android-powered device. If, however, you
+don't have a device, you can still test your location-based features by mocking location data in
+the Android emulator. There are three different ways to send your application mock location
+data: using Eclipse, DDMS, or the "geo" command in the emulator console.</p>
+
+<p class="note"><strong>Note:</strong> Providing mock location data is injected as GPS location
+data, so you must request location updates from <code>GPS_PROVIDER</code> in order for mock location
+data to work.</p>
+
+<h3 id="MockEclipse">Using Eclipse</h3>
+
+<p>Select <b>Window</b> &gt; <b>Show View</b> &gt; <b>Other</b> &gt; <b>Emulator Control</b>.</p>
+
+<p>In the Emulator Control panel, enter GPS coordinates under Location Controls as individual
+lat/long coordinates, with a GPX file for route playback, or a KML file for multiple place marks.
+(Be sure that you have a device selected in the Devices panel&mdash;available from <b>Window</b>
+&gt; <b>Show View</b> &gt; <b>Other</b> &gt; <b>Devices</b>.)</p>
+
+
+<h3 id="MockDdms">Using DDMS</h3>
+
+<p>With the DDMS tool, you can simulate location data a few different ways:</p>
+<ul>
+    <li>Manually send individual longitude/latitude coordinates to the device.</li>
+    <li>Use a GPX file describing a route for playback to the device.</li>
+    <li>Use a KML file describing individual place marks for sequenced playback to the device.</li>
+</ul>
+
+<p>For more information on using DDMS to spoof location data, see the
+<a href="{@docRoot}guide/developing/tools/ddms.html#emulator-control">Using DDMS guide</a>.
+
+
+<h3 id="MockGeo">Using the "geo" command in the emulator console</h3>
+
+<p>To send mock location data from the command line:</p>
+
+<ol>
+  <li>Launch your application in the Android emulator and open a terminal/console in your SDK's
+<code>/tools</code> directory.</li>
+  <li>Connect to the emulator console:
+<pre>telnet localhost <em>&lt;console-port&gt;</em></pre></li>
+  <li>Send the location data:</p>
+    <ul><li><code>geo fix</code> to send a fixed geo-location.
+    <p>This command accepts a longitude and latitude in decimal degrees, and
+    an optional altitude in meters. For example:</p>
+    <pre>geo fix -121.45356 46.51119 4392</pre>
+      </li>
+      <li><code>geo nmea</code> to send an NMEA 0183 sentence.
+    <p>This command accepts a single NMEA sentence of type '$GPGGA' (fix data) or '$GPRMC' (transit
+  data).
+    For example:</p>
+    <pre>geo nmea $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62</pre>
+      </li>
+    </ul>
+  </li>
+</ol>
+
+<p>For information about how to connect to the emulator console, see
+<a href="{@docRoot}guide/developing/tools/emulator.html#console">Using the Emulator Console</a>.</p>
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index de8ca6d..e030a4c 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -336,10 +336,10 @@
 </p></dd>
 
 <dt><a name="lmode"></a>{@code android:launchMode}</dt>
-<dd>An instruction on how the activity should be launched.  There are four modes 
+<dd>An instruction on how the activity should be launched.  There are four modes
 that work in conjunction with activity flags ({@code FLAG_ACTIVITY_*} constants) 
-in {@link android.content.Intent} objects to determine what should happen when 
-the activity is called upon to handle an intent.  They are:
+in {@link android.content.Intent} objects to determine what should happen when
+the activity is called upon to handle an intent. They are:</p>
 
 <p style="margin-left: 2em">"{@code standard}"
 <br>"{@code singleTop}"
@@ -351,56 +351,110 @@
 </p>
 
 <p>
-The modes fall into two main groups, with "{@code standard}" and 
-"{@code singleTop}" activities on one side, and "{@code singleTask}" and 
-"{@code singleInstance}" activities on the other.  An activity with the 
-"{@code standard}" or "{@code singleTop}" launch mode can be instantiated 
-multiple times.  The instances can belong to any task and can be located 
-anywhere in the activity stack.  Typically, they're launched into the task 
-that called 
+As shown in the table below, the modes fall into two main groups, with
+"{@code standard}" and "{@code singleTop}" activities on one side, and
+"{@code singleTask}" and "{@code singleInstance}" activities on the other.
+An activity with the "{@code standard}" or "{@code singleTop}" launch mode
+can be instantiated multiple times.  The instances can belong to any task
+and can be located anywhere in the activity stack.  Typically, they're
+launched into the task that called 
 <code>{@link android.content.Context#startActivity startActivity()}</code>
-(unless the Intent object contains a 
-<code>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</code> 
-instruction, in which case a different task is chosen &mdash; see the 
+(unless the Intent object contains a
+<code>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</code>
+instruction, in which case a different task is chosen &mdash; see the
 <a href="#aff">taskAffinity</a> attribute).
 </p>
 
 <p>
-In contrast, "{@code singleTask}" and "{@code singleInstance}" activities 
-can only begin a task.  They are always at the root of the activity stack. 
-Moreover, the device can hold only one instance of the activity at a time 
+In contrast, "<code>singleTask</code>" and "<code>singleInstance</code>" activities
+can only begin a task.  They are always at the root of the activity stack.
+Moreover, the device can hold only one instance of the activity at a time
 &mdash; only one such task.
 </p>
 
 <p>
 The "{@code standard}" and "{@code singleTop}" modes differ from each other 
-in just one respect:  Every time there's new intent for a "{@code standard}" 
-activity, a new instance of the class is created to respond to that intent.  
+in just one respect:  Every time there's a new intent for a "{@code standard}"
+activity, a new instance of the class is created to respond to that intent.
 Each instance handles a single intent.
-Similarly, a new instance of a "{@code singleTop}" activity may also be 
-created to handle a new intent.  However, if the target task already has an 
-existing instance of the activity at the top of its stack, that instance 
-will receive the new intent (in an 
+Similarly, a new instance of a "{@code singleTop}" activity may also be
+created to handle a new intent.  However, if the target task already has an
+existing instance of the activity at the top of its stack, that instance
+will receive the new intent (in an
 <code>{@link android.app.Activity#onNewIntent onNewIntent()}</code> call);
 a new instance is not created.
-In other circumstances &mdash; for example, if an existing instance of the 
-"{@code singleTop}" activity is in the target task, but not at the top of 
-the stack, or if it's at the top of a stack, but not in the target task 
+In other circumstances &mdash; for example, if an existing instance of the
+"{@code singleTop}" activity is in the target task, but not at the top of
+the stack, or if it's at the top of a stack, but not in the target task
 &mdash; a new instance would be created and pushed on the stack.
-</p>  
+</p>
 
 <p>
-The "{@code singleTask}" and "{@code singleInstance}" modes also differ from 
-each other in only one respect:  A "{@code singleTask}" activity allows other 
-activities to be part of its task.  It's at the root of the activity stack, 
-but other activities (necessarily "{@code standard}" and "{@code singleTop}" 
-activities) can be launched into the same task.  A "{@code singleInstance}" 
-activity, on the other hand, permits no other activities to be part of its 
-task.  It's the only activity in the task.  If it starts another activity, 
-that activity is assigned to a different task &mdash; as if {@code
+The "{@code singleTask}" and "{@code singleInstance}" modes also differ from
+each other in only one respect:  A "{@code singleTask}" activity allows other
+activities to be part of its task. It's always at the root of its task, but
+other activities (necessarily "{@code standard}" and "{@code singleTop}"
+activities) can be launched into that task.  A "{@code singleInstance}"
+activity, on the other hand, permits no other activities to be part of its task.
+It's the only activity in the task.  If it starts another activity, that
+activity is assigned to a different task &mdash; as if {@code
 FLAG_ACTIVITY_NEW_TASK} was in the intent.
 </p>
 
+<table>
+<tr>
+<th>Use Cases</th>
+<th>Launch Mode</th>
+<th>Multiple Instances?</th>
+<th>Comments</th>
+</tr>
+<tr>
+<td rowspan="2" style="width:20%;">Normal launches for most activities</td>
+<td>"<code>standard</code>"</td>
+<td>Yes</td>
+<td>Default. The system always creates a new instance of the activity in the
+target task and routes the intent to it.</td>
+</tr>
+<tr>
+<td>"<code>singleTop</code>"</td>
+<td>Conditionally</td>
+<td>If an instance of the activity already exists at the top of the target task,
+the system routes the intent to that instance through a call to its {@link
+android.app.Activity#onNewIntent onNewIntent()} method, rather than creating a
+new instance of the activity.</td>
+</tr>
+<tr>
+<td rowspan="2">Specialized launches<br>
+<em>(not recommended for general use)</em></td>
+<td>"<code>singleTask</code>"</td>
+<td>No</td>
+<td>The system creates the activity at the root of a new task and routes the
+intent to it. However, if an instance of the activity already exists, the system
+routes the intent to existing instance through a call to its {@link
+android.app.Activity#onNewIntent onNewIntent()} method, rather than creating a
+new one.</td>
+</tr>
+<tr>
+<td>"<code>singleInstance</code>"</td>
+<td>No</td>
+<td>Same as "<code>singleTask"</code>, except that the system doesn't launch any
+other activities into the task holding the instance. The activity is always the
+single and only member of its task.</td>
+</tr>
+</table>
+
+<p>As shown in the table above, <code>standard</code> is the default mode and is
+appropriate for most types of activities. <code>SingleTop</code> is also a
+common and useful launch mode for many types of activities. The other modes
+&mdash; <code>singleTask</code> and <code>singleInstance</code> &mdash; are
+<span style="color:red">not appropriate for most applications</span>,
+since they result in an interaction model that is likely to be unfamiliar to
+users and is very different from most other applications. 
+
+<p>Regardless of the launch mode that you choose, make sure to test the usability
+of the activity during launch and when navigating back to it from
+other activities and tasks using the BACK key. </p>
+
 <p>For more information on launch modes and their interaction with Intent
 flags, see the 
 <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Activities and 
diff --git a/docs/html/images/location/content-tagging.png b/docs/html/images/location/content-tagging.png
new file mode 100644
index 0000000..d58bfee
--- /dev/null
+++ b/docs/html/images/location/content-tagging.png
Binary files differ
diff --git a/docs/html/images/location/getting-location.png b/docs/html/images/location/getting-location.png
new file mode 100644
index 0000000..a5905ec
--- /dev/null
+++ b/docs/html/images/location/getting-location.png
Binary files differ
diff --git a/docs/html/images/location/where-to-go.png b/docs/html/images/location/where-to-go.png
new file mode 100644
index 0000000..59f5983
--- /dev/null
+++ b/docs/html/images/location/where-to-go.png
Binary files differ
diff --git a/docs/html/resources/samples/images/sample_notepadtest_junit.png b/docs/html/resources/samples/images/sample_notepadtest_junit.png
new file mode 100644
index 0000000..4cda54e
--- /dev/null
+++ b/docs/html/resources/samples/images/sample_notepadtest_junit.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/hello-formstuff.jd b/docs/html/resources/tutorials/views/hello-formstuff.jd
index 3dd5f21..b9f6c16 100644
--- a/docs/html/resources/tutorials/views/hello-formstuff.jd
+++ b/docs/html/resources/tutorials/views/hello-formstuff.jd
@@ -32,9 +32,19 @@
 }
 </pre>
 
+<p>Now select which kind of form widget you'd like to create:</p>
+<ul>
+  <li><a href="#CustomButton">Custom Button</a></li>
+  <li><a href="#EditText">Edit Text</a></li>
+  <li><a href="#Checkbox">Checkbox</a></li>
+  <li><a href="#RadioButtons">Radio Buttons</a></li>
+  <li><a href="#ToggleButton">Toggle Button</a></li>
+  <li><a href="#RatingBar">Rating Bar</a></li>
+</ul>
 
 
-<h2>Custom Button</h2>
+
+<h2 id="CustomButton">Custom Button</h2>
 
 <p>In this section, you will create a button with a custom image instead of text, using the {@link
 android.widget.Button} widget and an XML file that defines three different images to use for the
@@ -111,7 +121,8 @@
 </ol>
 
 
-<h2>EditText</h2>
+
+<h2 id="EditText">Edit Text</h2>
 
 <p>In this section, you will create a text field for user input, using the {@link
 android.widget.EditText} widget. Once text has been entered into the field, the "Enter" key will
@@ -158,7 +169,8 @@
 </ol>
 
 
-<h2>CheckBox</h2>
+
+<h2 id="Checkbox">Checkbox</h2>
 
 <p>In this section, you will create a checkbox for selecting items, using the {@link
 android.widget.CheckBox} widget. When the checkbox is pressed, a toast message will
@@ -209,7 +221,8 @@
 android.widget.CompoundButton#toggle()} method.</p>
 
 
-<h2>RadioButton</h2>
+
+<h2 id="RadioButtons">Radio Buttons</h2>
 
 <p>In this section, you will create two mutually-exclusive radio buttons (enabling one disables
 the other), using the {@link android.widget.RadioGroup} and {@link android.widget.RadioButton}
@@ -274,7 +287,8 @@
 android.widget.CompoundButton#toggle()} method.</p>
 
 
-<h2>ToggleButton</h2>
+
+<h2 id="ToggleButton">Toggle Button</h2>
 
 <p>In this section, you'll create a button used specifically for toggling between two
 states, using the {@link android.widget.ToggleButton} widget. This widget is an excellent
@@ -330,7 +344,7 @@
 
 
 
-<h2>RatingBar</h2>
+<h2 id="RatingBar">Rating Bar</h2>
 
 <p>In this section, you'll create a widget that allows the user to provide a rating,
 with the {@link android.widget.RatingBar} widget.</p>
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index f1f673b..6775c08 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -395,6 +395,17 @@
     static public Allocation createFromBitmapResourceBoxed(RenderScript rs, Resources res, int id, Element dstFmt, boolean genMips)
         throws IllegalArgumentException {
 
+        mBitmapOptions.inPreferredConfig = null;
+        if (dstFmt == rs.mElement_RGBA_8888) {
+            mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        } else if (dstFmt == rs.mElement_RGB_888) {
+            mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        } else if (dstFmt == rs.mElement_RGBA_4444) {
+            mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_4444;
+        } else if (dstFmt == rs.mElement_RGB_565) {
+            mBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
+        }
+
         Bitmap b = BitmapFactory.decodeResource(res, id, mBitmapOptions);
         return createFromBitmapBoxed(rs, b, dstFmt, genMips);
     }
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index b811479..ed09f89 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -54,16 +54,20 @@
         UNSIGNED_5_5_5_1 (14, 2),
         UNSIGNED_4_4_4_4 (15, 2),
 
-        RS_ELEMENT (16, 4),
-        RS_TYPE (17, 4),
-        RS_ALLOCATION (18, 4),
-        RS_SAMPLER (19, 4),
-        RS_SCRIPT (20, 4),
-        RS_MESH (21, 4),
-        RS_PROGRAM_FRAGMENT (22, 4),
-        RS_PROGRAM_VERTEX (23, 4),
-        RS_PROGRAM_RASTER (24, 4),
-        RS_PROGRAM_STORE (25, 4);
+        MATRIX_4X4 (16, 64),
+        MATRIX_3X3 (17, 36),
+        MATRIX_2X2 (18, 16),
+
+        RS_ELEMENT (1000, 4),
+        RS_TYPE (1001, 4),
+        RS_ALLOCATION (1002, 4),
+        RS_SAMPLER (1003, 4),
+        RS_SCRIPT (1004, 4),
+        RS_MESH (1005, 4),
+        RS_PROGRAM_FRAGMENT (1006, 4),
+        RS_PROGRAM_VERTEX (1007, 4),
+        RS_PROGRAM_RASTER (1008, 4),
+        RS_PROGRAM_STORE (1009, 4);
 
         int mID;
         int mSize;
@@ -285,6 +289,29 @@
         return rs.mElement_UCHAR_4;
     }
 
+    public static Element MATRIX_4X4(RenderScript rs) {
+        if(rs.mElement_MATRIX_4X4 == null) {
+            rs.mElement_MATRIX_4X4 = createUser(rs, DataType.MATRIX_4X4);
+        }
+        return rs.mElement_MATRIX_4X4;
+    }
+    public static Element MATRIX4X4(RenderScript rs) {
+        return MATRIX_4X4(rs);
+    }
+
+    public static Element MATRIX_3X3(RenderScript rs) {
+        if(rs.mElement_MATRIX_3X3 == null) {
+            rs.mElement_MATRIX_3X3 = createUser(rs, DataType.MATRIX_3X3);
+        }
+        return rs.mElement_MATRIX_4X4;
+    }
+
+    public static Element MATRIX_2X2(RenderScript rs) {
+        if(rs.mElement_MATRIX_2X2 == null) {
+            rs.mElement_MATRIX_2X2 = createUser(rs, DataType.MATRIX_2X2);
+        }
+        return rs.mElement_MATRIX_2X2;
+    }
 
     Element(int id, RenderScript rs, Element[] e, String[] n) {
         super(id, rs);
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index 24f0409..b6f88be 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -248,6 +248,24 @@
         addU32(v.w);
     }
 
+    public void addObj(Matrix4f v) {
+        for (int i=0; i < v.mMat.length; i++) {
+            addF32(v.mMat[i]);
+        }
+    }
+
+    public void addObj(Matrix3f v) {
+        for (int i=0; i < v.mMat.length; i++) {
+            addF32(v.mMat[i]);
+        }
+    }
+
+    public void addObj(Matrix2f v) {
+        for (int i=0; i < v.mMat.length; i++) {
+            addF32(v.mMat[i]);
+        }
+    }
+
     public void addBoolean(boolean v) {
         addI8((byte)(v ? 1 : 0));
     }
diff --git a/graphics/java/android/renderscript/ProgramRaster.java b/graphics/java/android/renderscript/ProgramRaster.java
index 08065cf..fd89b6e 100644
--- a/graphics/java/android/renderscript/ProgramRaster.java
+++ b/graphics/java/android/renderscript/ProgramRaster.java
@@ -67,17 +67,46 @@
         mRS.nProgramRasterSetCullMode(mID, m.mID);
     }
 
+    public static ProgramRaster CULL_BACK(RenderScript rs) {
+        if(rs.mProgramRaster_CULL_BACK == null) {
+            ProgramRaster.Builder builder = new ProgramRaster.Builder(rs);
+            builder.setCullMode(CullMode.BACK);
+            rs.mProgramRaster_CULL_BACK = builder.create();
+        }
+        return rs.mProgramRaster_CULL_BACK;
+    }
+
+    public static ProgramRaster CULL_FRONT(RenderScript rs) {
+        if(rs.mProgramRaster_CULL_FRONT == null) {
+            ProgramRaster.Builder builder = new ProgramRaster.Builder(rs);
+            builder.setCullMode(CullMode.FRONT);
+            rs.mProgramRaster_CULL_FRONT = builder.create();
+        }
+        return rs.mProgramRaster_CULL_FRONT;
+    }
+
+    public static ProgramRaster CULL_NONE(RenderScript rs) {
+        if(rs.mProgramRaster_CULL_NONE == null) {
+            ProgramRaster.Builder builder = new ProgramRaster.Builder(rs);
+            builder.setCullMode(CullMode.NONE);
+            rs.mProgramRaster_CULL_NONE = builder.create();
+        }
+        return rs.mProgramRaster_CULL_NONE;
+    }
+
     public static class Builder {
         RenderScript mRS;
         boolean mPointSprite;
         boolean mPointSmooth;
         boolean mLineSmooth;
+        CullMode mCullMode;
 
         public Builder(RenderScript rs) {
             mRS = rs;
             mPointSmooth = false;
             mLineSmooth = false;
             mPointSprite = false;
+            mCullMode = CullMode.BACK;
         }
 
         public Builder setPointSpriteEnable(boolean enable) {
@@ -95,9 +124,15 @@
             return this;
         }
 
+        public Builder setCullMode(CullMode m) {
+            mCullMode = m;
+            return this;
+        }
+
         static synchronized ProgramRaster internalCreate(RenderScript rs, Builder b) {
             int id = rs.nProgramRasterCreate(b.mPointSmooth, b.mLineSmooth, b.mPointSprite);
             ProgramRaster pr = new ProgramRaster(id, rs);
+            pr.setCullMode(b.mCullMode);
             return pr;
         }
 
diff --git a/graphics/java/android/renderscript/ProgramStore.java b/graphics/java/android/renderscript/ProgramStore.java
index cce4064..32c0d01 100644
--- a/graphics/java/android/renderscript/ProgramStore.java
+++ b/graphics/java/android/renderscript/ProgramStore.java
@@ -79,7 +79,140 @@
         super(id, rs);
     }
 
+    public static ProgramStore BLEND_NONE_DEPTH_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_NONE_DEPTH_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.LESS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_NONE_DEPTH_TEST = builder.create();
+        }
+        return rs.mProgramStore_BLEND_NONE_DEPTH_TEST;
+    }
+    public static ProgramStore BLEND_NONE_DEPTH_NO_DEPTH(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_NONE_DEPTH_NO_DEPTH == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_NONE_DEPTH_NO_DEPTH = builder.create();
+        }
+        return rs.mProgramStore_BLEND_NONE_DEPTH_NO_DEPTH;
+    }
+    public static ProgramStore BLEND_NONE_DEPTH_NO_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_NONE_DEPTH_NO_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_NONE_DEPTH_NO_TEST = builder.create();
+        }
+        return rs.mProgramStore_BLEND_NONE_DEPTH_NO_TEST;
+    }
+    public static ProgramStore BLEND_NONE_DEPTH_NO_WRITE(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_NONE_DEPTH_NO_WRITE == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.LESS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_NONE_DEPTH_NO_WRITE = builder.create();
+        }
+        return rs.mProgramStore_BLEND_NONE_DEPTH_NO_WRITE;
+    }
 
+    public static ProgramStore BLEND_ALPHA_DEPTH_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ALPHA_DEPTH_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.LESS);
+            builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_ALPHA_DEPTH_TEST = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ALPHA_DEPTH_TEST;
+    }
+    public static ProgramStore BLEND_ALPHA_DEPTH_NO_DEPTH(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_DEPTH == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_DEPTH = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_DEPTH;
+    }
+    public static ProgramStore BLEND_ALPHA_DEPTH_NO_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_TEST = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_TEST;
+    }
+    public static ProgramStore BLEND_ALPHA_DEPTH_NO_WRITE(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_WRITE == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.LESS);
+            builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_WRITE = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_WRITE;
+    }
+
+    public static ProgramStore BLEND_ADD_DEPTH_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ADD_DEPTH_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.LESS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_ADD_DEPTH_TEST = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ADD_DEPTH_TEST;
+    }
+    public static ProgramStore BLEND_ADD_DEPTH_NO_DEPTH(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ADD_DEPTH_NO_DEPTH == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_ADD_DEPTH_NO_DEPTH = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ADD_DEPTH_NO_DEPTH;
+    }
+    public static ProgramStore BLEND_ADD_DEPTH_NO_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ADD_DEPTH_NO_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_ADD_DEPTH_NO_DEPTH = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ADD_DEPTH_NO_TEST;
+    }
+    public static ProgramStore BLEND_ADD_DEPTH_NO_WRITE(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ADD_DEPTH_NO_WRITE == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_ADD_DEPTH_NO_WRITE = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ADD_DEPTH_NO_WRITE;
+    }
 
     public static class Builder {
         RenderScript mRS;
@@ -109,8 +242,20 @@
             mColorMaskA = true;
             mBlendSrc = BlendSrcFunc.ONE;
             mBlendDst = BlendDstFunc.ZERO;
+        }
 
-
+        public Builder(RenderScript rs) {
+            mRS = rs;
+            mIn = null;
+            mOut = null;
+            mDepthFunc = DepthFunc.ALWAYS;
+            mDepthMask = false;
+            mColorMaskR = true;
+            mColorMaskG = true;
+            mColorMaskB = true;
+            mColorMaskA = true;
+            mBlendSrc = BlendSrcFunc.ONE;
+            mBlendDst = BlendDstFunc.ZERO;
         }
 
         public Builder setDepthFunc(DepthFunc func) {
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index ec377e2..c99efd6 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -46,6 +46,9 @@
         public Builder(RenderScript rs, Element in, Element out) {
             mRS = rs;
         }
+        public Builder(RenderScript rs) {
+            mRS = rs;
+        }
 
         public Builder setTextureMatrixEnable(boolean enable) {
             mTextureMatrixEnable = enable;
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index d82e524..08ba7e2 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -542,6 +542,10 @@
     Element mElement_FLOAT_4;
     Element mElement_UCHAR_4;
 
+    Element mElement_MATRIX_4X4;
+    Element mElement_MATRIX_3X3;
+    Element mElement_MATRIX_2X2;
+
     Sampler mSampler_CLAMP_NEAREST;
     Sampler mSampler_CLAMP_LINEAR;
     Sampler mSampler_CLAMP_LINEAR_MIP_LINEAR;
@@ -549,6 +553,23 @@
     Sampler mSampler_WRAP_LINEAR;
     Sampler mSampler_WRAP_LINEAR_MIP_LINEAR;
 
+    ProgramStore mProgramStore_BLEND_NONE_DEPTH_TEST;
+    ProgramStore mProgramStore_BLEND_NONE_DEPTH_NO_DEPTH;
+    ProgramStore mProgramStore_BLEND_NONE_DEPTH_NO_TEST;
+    ProgramStore mProgramStore_BLEND_NONE_DEPTH_NO_WRITE;
+    ProgramStore mProgramStore_BLEND_ALPHA_DEPTH_TEST;
+    ProgramStore mProgramStore_BLEND_ALPHA_DEPTH_NO_DEPTH;
+    ProgramStore mProgramStore_BLEND_ALPHA_DEPTH_NO_TEST;
+    ProgramStore mProgramStore_BLEND_ALPHA_DEPTH_NO_WRITE;
+    ProgramStore mProgramStore_BLEND_ADD_DEPTH_TEST;
+    ProgramStore mProgramStore_BLEND_ADD_DEPTH_NO_DEPTH;
+    ProgramStore mProgramStore_BLEND_ADD_DEPTH_NO_TEST;
+    ProgramStore mProgramStore_BLEND_ADD_DEPTH_NO_WRITE;
+
+    ProgramRaster mProgramRaster_CULL_BACK;
+    ProgramRaster mProgramRaster_CULL_FRONT;
+    ProgramRaster mProgramRaster_CULL_NONE;
+
     ///////////////////////////////////////////////////////////////////////////////////
     //
 
@@ -597,15 +618,22 @@
             int[] rbuf = new int[16];
             mRS.nContextInitToClient(mRS.mContext);
             while(mRun) {
+                rbuf[0] = 0;
                 int msg = mRS.nContextGetMessage(mRS.mContext, rbuf, true);
                 if (msg == 0) {
-                    // Should only happen during teardown.
-                    // But we want to avoid starving other threads during
-                    // teardown by yielding until the next line in the destructor
-                    // can execute to set mRun = false
-                    try {
-                        sleep(1, 0);
-                    } catch(InterruptedException e) {
+                    // Can happen for two reasons
+                    if (rbuf[0] > 0) {
+                        // 1: Buffer needs to be enlarged.
+                        rbuf = new int[rbuf[0] + 2];
+                    } else {
+                        // 2: teardown.
+                        // But we want to avoid starving other threads during
+                        // teardown by yielding until the next line in the destructor
+                        // can execute to set mRun = false
+                        try {
+                            sleep(1, 0);
+                        } catch(InterruptedException e) {
+                        }
                     }
                 }
                 if(mRS.mMessageCallback != null) {
diff --git a/graphics/java/android/renderscript/Sampler.java b/graphics/java/android/renderscript/Sampler.java
index 60c6cf4..343fcdb 100644
--- a/graphics/java/android/renderscript/Sampler.java
+++ b/graphics/java/android/renderscript/Sampler.java
@@ -50,13 +50,6 @@
         super(id, rs);
     }
 
-    Sampler mSampler_CLAMP_NEAREST;
-    Sampler mSampler_CLAMP_LINEAR;
-    Sampler mSampler_CLAMP_LINEAR_MIP;
-    Sampler mSampler_WRAP_NEAREST;
-    Sampler mSampler_WRAP_LINEAR;
-    Sampler mSampler_WRAP_LINEAR_MIP;
-
     public static Sampler CLAMP_NEAREST(RenderScript rs) {
         if(rs.mSampler_CLAMP_NEAREST == null) {
             Builder b = new Builder(rs);
@@ -85,7 +78,7 @@
         if(rs.mSampler_CLAMP_LINEAR_MIP_LINEAR == null) {
             Builder b = new Builder(rs);
             b.setMin(Value.LINEAR_MIP_LINEAR);
-            b.setMag(Value.LINEAR_MIP_LINEAR);
+            b.setMag(Value.LINEAR);
             b.setWrapS(Value.CLAMP);
             b.setWrapT(Value.CLAMP);
             rs.mSampler_CLAMP_LINEAR_MIP_LINEAR = b.create();
@@ -121,7 +114,7 @@
         if(rs.mSampler_WRAP_LINEAR_MIP_LINEAR == null) {
             Builder b = new Builder(rs);
             b.setMin(Value.LINEAR_MIP_LINEAR);
-            b.setMag(Value.LINEAR_MIP_LINEAR);
+            b.setMag(Value.LINEAR);
             b.setWrapS(Value.WRAP);
             b.setWrapT(Value.WRAP);
             rs.mSampler_WRAP_LINEAR_MIP_LINEAR = b.create();
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 3624678..81f08e9 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -37,6 +37,7 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_view_Surface.h"
 
 #include <RenderScript.h>
 #include <RenderScriptEnv.h>
@@ -184,9 +185,7 @@
     if (wnd == NULL) {
 
     } else {
-        jclass surface_class = _env->FindClass("android/view/Surface");
-        jfieldID surfaceFieldID = _env->GetFieldID(surface_class, ANDROID_VIEW_SURFACE_JNI_ID, "I");
-        window = (Surface*)_env->GetIntField(wnd, surfaceFieldID);
+        window = (Surface*) android_Surface_getNativeWindow(_env, wnd).get();
     }
 
     rsContextSetSurface(con, width, height, window);
@@ -229,7 +228,8 @@
     size_t receiveLen;
     int id = rsContextGetMessage(con, ptr, &receiveLen, len * 4, wait);
     if (!id && receiveLen) {
-        LOGE("message receive buffer too small.  %i", receiveLen);
+        LOGV("message receive buffer too small.  %i", receiveLen);
+        *ptr = (jint)receiveLen;
     }
     _env->ReleaseIntArrayElements(data, ptr, 0);
     return id;
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index 964700b..75cf5ff 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -22,8 +22,6 @@
 
 namespace android {
 
-class ISurface;
-
 /*
  * A set of bit masks for specifying how the received preview frames are
  * handled before the previewCallback() call.
@@ -152,9 +150,8 @@
 
             status_t    getStatus() { return mStatus; }
 
-            // pass the buffered ISurface to the camera service
+            // pass the buffered Surface to the camera service
             status_t    setPreviewDisplay(const sp<Surface>& surface);
-            status_t    setPreviewDisplay(const sp<ISurface>& surface);
 
             // start preview mode, must call setPreviewDisplay first
             status_t    startPreview();
diff --git a/include/camera/CameraHardwareInterface.h b/include/camera/CameraHardwareInterface.h
index 1529db7..515d879 100644
--- a/include/camera/CameraHardwareInterface.h
+++ b/include/camera/CameraHardwareInterface.h
@@ -18,6 +18,7 @@
 #define ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
 
 #include <binder/IMemory.h>
+#include <ui/egl/android_natives.h>
 #include <utils/RefBase.h>
 #include <surfaceflinger/ISurface.h>
 #include <camera/Camera.h>
@@ -86,8 +87,8 @@
 public:
     virtual ~CameraHardwareInterface() { }
 
-    /** Return the IMemoryHeap for the preview image heap */
-    virtual sp<IMemoryHeap>         getPreviewHeap() const = 0;
+    /** Set the ISurface from which the preview buffers should be dequeued */
+    virtual status_t setPreviewWindow(const sp<ANativeWindow>& buf) = 0;
 
     /** Return the IMemoryHeap for the raw image heap */
     virtual sp<IMemoryHeap>         getRawHeap() const = 0;
@@ -221,6 +222,7 @@
  */
 extern "C" int HAL_getNumberOfCameras();
 extern "C" void HAL_getCameraInfo(int cameraId, struct CameraInfo* cameraInfo);
+/* HAL should return NULL if it fails to open camera hardware. */
 extern "C" sp<CameraHardwareInterface> HAL_openCameraHardware(int cameraId);
 
 };  // namespace android
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 4bc1799..a5c7874 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -61,6 +61,7 @@
     void getSupportedPreviewSizes(Vector<Size> &sizes) const;
     void setPreviewFrameRate(int fps);
     int getPreviewFrameRate() const;
+    void getPreviewFpsRange(int *min_fps, int *max_fps) const;
     void setPreviewFormat(const char *format);
     const char *getPreviewFormat() const;
     void setPictureSize(int width, int height);
@@ -82,6 +83,20 @@
     // Supported preview frame sizes in pixels.
     // Example value: "800x600,480x320". Read only.
     static const char KEY_SUPPORTED_PREVIEW_SIZES[];
+    // The current minimum and maximum preview fps. This controls the rate of
+    // preview frames received (CAMERA_MSG_PREVIEW_FRAME). The minimum and
+    // maximum fps must be one of the elements from
+    // KEY_SUPPORTED_PREVIEW_FPS_RANGE parameter.
+    // Example value: "10500,26623"
+    static const char KEY_PREVIEW_FPS_RANGE[];
+    // The supported preview fps (frame-per-second) ranges. Each range contains
+    // a minimum fps and maximum fps. If minimum fps equals to maximum fps, the
+    // camera outputs frames in fixed frame rate. If not, the camera outputs
+    // frames in auto frame rate. The actual frame rate fluctuates between the
+    // minimum and the maximum. The list has at least one element. The list is
+    // sorted from small to large (first by maximum fps and then minimum fps).
+    // Example value: "(10500,26623),(15000,26623),(30000,30000)"
+    static const char KEY_SUPPORTED_PREVIEW_FPS_RANGE[];
     // The image format for preview frames. See CAMERA_MSG_PREVIEW_FRAME in
     // frameworks/base/include/camera/Camera.h.
     // Example value: "yuv420sp" or PIXEL_FORMAT_XXX constants. Read/write.
diff --git a/include/camera/ICamera.h b/include/camera/ICamera.h
index 6fcf9e5..8bceea5 100644
--- a/include/camera/ICamera.h
+++ b/include/camera/ICamera.h
@@ -20,7 +20,7 @@
 #include <utils/RefBase.h>
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
-#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
 #include <binder/IMemory.h>
 #include <utils/String8.h>
 #include <camera/Camera.h>
@@ -45,8 +45,8 @@
     // allow other processes to use this ICamera interface
     virtual status_t        unlock() = 0;
 
-    // pass the buffered ISurface to the camera service
-    virtual status_t        setPreviewDisplay(const sp<ISurface>& surface) = 0;
+    // pass the buffered Surface to the camera service
+    virtual status_t        setPreviewDisplay(const sp<Surface>& surface) = 0;
 
     // set the preview callback flag to affect how the received frames from
     // preview are handled.
diff --git a/include/media/EffectPresetReverbApi.h b/include/media/EffectPresetReverbApi.h
index 53205bb..a3f094c 100644
--- a/include/media/EffectPresetReverbApi.h
+++ b/include/media/EffectPresetReverbApi.h
@@ -43,7 +43,8 @@
     REVERB_PRESET_LARGEROOM,
     REVERB_PRESET_MEDIUMHALL,
     REVERB_PRESET_LARGEHALL,
-    REVERB_PRESET_PLATE
+    REVERB_PRESET_PLATE,
+    REVERB_PRESET_LAST = REVERB_PRESET_PLATE
 } t_reverb_presets;
 
 #if __cplusplus
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index af9a7ed..a1ce113 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -25,6 +25,7 @@
 
 class Parcel;
 class ISurface;
+class Surface;
 
 class IMediaPlayer: public IInterface
 {
@@ -33,7 +34,8 @@
 
     virtual void            disconnect() = 0;
 
-    virtual status_t        setVideoSurface(const sp<ISurface>& surface) = 0;
+    virtual status_t        setVideoISurface(const sp<ISurface>& surface) = 0;
+    virtual status_t        setVideoSurface(const sp<Surface>& surface) = 0;
     virtual status_t        prepareAsync() = 0;
     virtual status_t        start() = 0;
     virtual status_t        stop() = 0;
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 54adca8..54b197c 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -22,7 +22,7 @@
 
 namespace android {
 
-class ISurface;
+class Surface;
 class ICamera;
 class IMediaRecorderClient;
 
@@ -32,7 +32,7 @@
     DECLARE_META_INTERFACE(MediaRecorder);
 
     virtual	status_t		setCamera(const sp<ICamera>& camera) = 0;
-    virtual	status_t		setPreviewSurface(const sp<ISurface>& surface) = 0;
+    virtual	status_t		setPreviewSurface(const sp<Surface>& surface) = 0;
     virtual	status_t		setVideoSource(int vs) = 0;
     virtual	status_t		setAudioSource(int as) = 0;
     virtual	status_t		setOutputFormat(int of) = 0;
@@ -68,4 +68,3 @@
 }; // namespace android
 
 #endif // ANDROID_IMEDIARECORDER_H
-
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 3662983..13c73ac 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -33,6 +33,7 @@
 
 class Parcel;
 class ISurface;
+class Surface;
 
 template<typename T> class SortedVector;
 
@@ -104,7 +105,8 @@
             const KeyedVector<String8, String8> *headers = NULL) = 0;
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length) = 0;
-    virtual status_t    setVideoSurface(const sp<ISurface>& surface) = 0;
+    virtual status_t    setVideoISurface(const sp<ISurface>& surface) = 0;
+    virtual status_t    setVideoSurface(const sp<Surface>& surface) = 0;
     virtual status_t    prepare() = 0;
     virtual status_t    prepareAsync() = 0;
     virtual status_t    start() = 0;
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index 5e9e368..e5edd29 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -22,7 +22,7 @@
 
 namespace android {
 
-class ISurface;
+class Surface;
 
 struct MediaRecorderBase {
     MediaRecorderBase() {}
@@ -37,7 +37,7 @@
     virtual status_t setVideoSize(int width, int height) = 0;
     virtual status_t setVideoFrameRate(int frames_per_second) = 0;
     virtual status_t setCamera(const sp<ICamera>& camera) = 0;
-    virtual status_t setPreviewSurface(const sp<ISurface>& surface) = 0;
+    virtual status_t setPreviewSurface(const sp<Surface>& surface) = 0;
     virtual status_t setOutputFile(const char *path) = 0;
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0;
     virtual status_t setParameters(const String8& params) = 0;
diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h
index c091c39..4b44ccc 100644
--- a/include/media/PVMediaRecorder.h
+++ b/include/media/PVMediaRecorder.h
@@ -23,7 +23,7 @@
 
 namespace android {
 
-class ISurface;
+class Surface;
 class ICamera;
 class AuthorDriverWrapper;
 
@@ -41,7 +41,7 @@
     virtual status_t setVideoSize(int width, int height);
     virtual status_t setVideoFrameRate(int frames_per_second);
     virtual status_t setCamera(const sp<ICamera>& camera);
-    virtual status_t setPreviewSurface(const sp<ISurface>& surface);
+    virtual status_t setPreviewSurface(const sp<Surface>& surface);
     virtual status_t setOutputFile(const char *path);
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
     virtual status_t setParameters(const String8& params);
@@ -66,4 +66,3 @@
 }; // namespace android
 
 #endif // ANDROID_PVMEDIARECORDER_H
-
diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h
index df50981..657e7a6 100644
--- a/include/media/PVPlayer.h
+++ b/include/media/PVPlayer.h
@@ -43,7 +43,8 @@
             const char *url, const KeyedVector<String8, String8> *headers);
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
-    virtual status_t    setVideoSurface(const sp<ISurface>& surface);
+    virtual status_t    setVideoISurface(const sp<ISurface>& surface);
+    virtual status_t    setVideoSurface(const sp<Surface>& surface);
     virtual status_t    prepare();
     virtual status_t    prepareAsync();
     virtual status_t    start();
diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h
index 5d51de8..b8746c2 100644
--- a/include/media/Visualizer.h
+++ b/include/media/Visualizer.h
@@ -151,7 +151,6 @@
     void *mCaptureCbkUser;
     sp<CaptureThread> mCaptureThread;
     uint32_t mCaptureFlags;
-    void *mFftTable;
 };
 
 
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index 813dd43..aa965e1 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -37,8 +37,8 @@
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual bool reachedEOS();
     virtual status_t start(MetaData *params = NULL);
-    virtual void stop();
-    virtual void pause();
+    virtual status_t stop();
+    virtual status_t pause();
 
 protected:
     virtual ~AMRWriter();
@@ -57,7 +57,7 @@
     int64_t mEstimatedDurationUs;
 
     static void *ThreadWrapper(void *);
-    void threadFunc();
+    status_t threadFunc();
     bool exceedsFileSizeLimit();
     bool exceedsFileDurationLimit();
 
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 6f7dc38..9d2cff6 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -28,6 +28,7 @@
 
 namespace android {
 
+struct AMessage;
 class String8;
 
 class DataSource : public RefBase {
@@ -59,10 +60,14 @@
 
     ////////////////////////////////////////////////////////////////////////////
 
-    bool sniff(String8 *mimeType, float *confidence);
+    bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta);
 
+    // The sniffer can optionally fill in "meta" with an AMessage containing
+    // a dictionary of values that helps the corresponding extractor initialize
+    // its state without duplicating effort already exerted by the sniffer.
     typedef bool (*SnifferFunc)(
-            const sp<DataSource> &source, String8 *mimeType, float *confidence);
+            const sp<DataSource> &source, String8 *mimeType,
+            float *confidence, sp<AMessage> *meta);
 
     static void RegisterSniffer(SnifferFunc func);
     static void RegisterDefaultSniffers();
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index be96935..2412f6a 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -37,9 +37,10 @@
 
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual status_t start(MetaData *param = NULL);
+    virtual status_t stop();
+    virtual status_t pause();
     virtual bool reachedEOS();
-    virtual void stop();
-    virtual void pause();
+    virtual status_t dump(int fd, const Vector<String16>& args);
 
     void beginBox(const char *fourcc);
     void writeInt8(int8_t x);
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index 8d3a9df..5cc8dcf 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -35,14 +35,19 @@
     virtual status_t addSource(const sp<MediaSource> &source) = 0;
     virtual bool reachedEOS() = 0;
     virtual status_t start(MetaData *params = NULL) = 0;
-    virtual void stop() = 0;
-    virtual void pause() = 0;
+    virtual status_t stop() = 0;
+    virtual status_t pause() = 0;
+
     virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }
     virtual void setMaxFileDuration(int64_t durationUs) { mMaxFileDurationLimitUs = durationUs; }
     virtual void setListener(const sp<IMediaRecorderClient>& listener) {
         mListener = listener;
     }
 
+    virtual status_t dump(int fd, const Vector<String16>& args) {
+        return OK;
+    }
+
 protected:
     virtual ~MediaWriter() {}
     int64_t mMaxFileSizeLimitBytes;
diff --git a/media/libstagefright/mpeg2ts/ABitReader.h b/include/media/stagefright/foundation/ABitReader.h
similarity index 100%
rename from media/libstagefright/mpeg2ts/ABitReader.h
rename to include/media/stagefright/foundation/ABitReader.h
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index 1eb178e..d016dfa 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -54,11 +54,6 @@
 
 // ----------------------------------------------------------------------------
 
-// 4 * (11 + 7 + (1 + 2*NUM_RECT_MAX) * NUM_BUFFER_MAX) * NUM_LAYERS_MAX
-// 4 * (11 + 7 + (1 + 2*7)*16) * 31
-// 1032 * 31
-// = ~27 KiB (31992)
-
 class SharedBufferStack
 {
     friend class SharedClient;
@@ -85,7 +80,7 @@
     };
 
     struct FlatRegion { // 52 bytes = 4 * (1 + 2*N)
-        static const unsigned int NUM_RECT_MAX = 6;
+        static const unsigned int NUM_RECT_MAX = 5;
         uint32_t    count;
         SmallRect   rects[NUM_RECT_MAX];
     };
@@ -93,13 +88,18 @@
     struct BufferData {
         FlatRegion dirtyRegion;
         SmallRect  crop;
+        uint8_t transform;
+        uint8_t reserved[3];
     };
     
     SharedBufferStack();
     void init(int32_t identity);
     status_t setDirtyRegion(int buffer, const Region& reg);
     status_t setCrop(int buffer, const Rect& reg);
+    status_t setTransform(int buffer, uint8_t transform);
     Region getDirtyRegion(int buffer) const;
+    Rect getCrop(int buffer) const;
+    uint32_t getTransform(int buffer) const;
 
     // these attributes are part of the conditions/updates
     volatile int32_t head;      // server's current front buffer
@@ -117,7 +117,7 @@
     int32_t     reserved32[1];
     Statistics  stats;
     int32_t     reserved;
-    BufferData  buffers[NUM_BUFFER_MAX];     // 960 bytes
+    BufferData  buffers[NUM_BUFFER_MAX];     // 1024 bytes
 };
 
 // ----------------------------------------------------------------------------
@@ -206,7 +206,7 @@
     bool needNewBuffer(int buffer) const;
     status_t setDirtyRegion(int buffer, const Region& reg);
     status_t setCrop(int buffer, const Rect& reg);
-    
+    status_t setTransform(int buffer, uint32_t transform);
 
     class SetBufferCountCallback {
         friend class SharedBufferClient;
@@ -275,6 +275,8 @@
     status_t reallocateAllExcept(int buffer);
     int32_t getQueuedCount() const;
     Region getDirtyRegion(int buffer) const;
+    Rect getCrop(int buffer) const;
+    uint32_t getTransform(int buffer) const;
 
     status_t resize(int newNumBuffers);
 
diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h
new file mode 100644
index 0000000..a2735a4
--- /dev/null
+++ b/include/storage/IMountService.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IMOUNTSERVICE_H
+#define ANDROID_IMOUNTSERVICE_H
+
+#include <storage/IMountServiceListener.h>
+#include <storage/IMountShutdownObserver.h>
+#include <storage/IObbActionListener.h>
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IMountService: public IInterface {
+public:
+    DECLARE_META_INTERFACE(MountService);
+
+    virtual void registerListener(const sp<IMountServiceListener>& listener) = 0;
+    virtual void
+            unregisterListener(const sp<IMountServiceListener>& listener) = 0;
+    virtual bool isUsbMassStorageConnected() = 0;
+    virtual void setUsbMassStorageEnabled(const bool enable) = 0;
+    virtual bool isUsbMassStorageEnabled() = 0;
+    virtual int32_t mountVolume(const String16& mountPoint) = 0;
+    virtual int32_t
+            unmountVolume(const String16& mountPoint, const bool force) = 0;
+    virtual int32_t formatVolume(const String16& mountPoint) = 0;
+    virtual int32_t
+            getStorageUsers(const String16& mountPoint, int32_t** users) = 0;
+    virtual int32_t getVolumeState(const String16& mountPoint) = 0;
+    virtual int32_t createSecureContainer(const String16& id,
+            const int32_t sizeMb, const String16& fstype, const String16& key,
+            const int32_t ownerUid) = 0;
+    virtual int32_t finalizeSecureContainer(const String16& id) = 0;
+    virtual int32_t destroySecureContainer(const String16& id) = 0;
+    virtual int32_t mountSecureContainer(const String16& id,
+            const String16& key, const int32_t ownerUid) = 0;
+    virtual int32_t
+            unmountSecureContainer(const String16& id, const bool force) = 0;
+    virtual bool isSecureContainerMounted(const String16& id) = 0;
+    virtual int32_t renameSecureContainer(const String16& oldId,
+            const String16& newId) = 0;
+    virtual bool getSecureContainerPath(const String16& id, String16& path) = 0;
+    virtual int32_t getSecureContainerList(const String16& id,
+            String16*& containers) = 0;
+    virtual void shutdown(const sp<IMountShutdownObserver>& observer) = 0;
+    virtual void finishMediaUpdate() = 0;
+    virtual void mountObb(const String16& filename, const String16& key,
+            const sp<IObbActionListener>& token) = 0;
+    virtual void unmountObb(const String16& filename, const bool force) = 0;
+    virtual bool isObbMounted(const String16& filename) = 0;
+    virtual bool getMountedObbPath(const String16& filename, String16& path) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMountService: public BnInterface<IMountService> {
+public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+            Parcel* reply, uint32_t flags = 0);
+};
+
+}
+; // namespace android
+
+#endif // ANDROID_IMOUNTSERVICE_H
diff --git a/include/storage/IMountServiceListener.h b/include/storage/IMountServiceListener.h
new file mode 100644
index 0000000..5b1f21c
--- /dev/null
+++ b/include/storage/IMountServiceListener.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IMOUNTSERVICELISTENER_H
+#define ANDROID_IMOUNTSERVICELISTENER_H
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IMountServiceListener: public IInterface {
+public:
+    DECLARE_META_INTERFACE(MountServiceListener);
+
+    virtual void onUsbMassStorageConnectionChanged(const bool connected) = 0;
+    virtual void onStorageStateChanged(const String16& path,
+            const String16& oldState, const String16& newState) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMountServiceListener: public BnInterface<IMountServiceListener> {
+public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+            Parcel* reply, uint32_t flags = 0);
+};
+
+}
+; // namespace android
+
+#endif // ANDROID_IMOUNTSERVICELISTENER_H
diff --git a/include/storage/IMountShutdownObserver.h b/include/storage/IMountShutdownObserver.h
new file mode 100644
index 0000000..d019e01
--- /dev/null
+++ b/include/storage/IMountShutdownObserver.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IMOUNTSHUTDOWNOBSERVER_H
+#define ANDROID_IMOUNTSHUTDOWNOBSERVER_H
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IMountShutdownObserver: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(MountShutdownObserver);
+
+    virtual void onShutDownComplete(const int32_t statusCode) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMountShutdownObserver: public BnInterface<IMountShutdownObserver>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IMOUNTSHUTDOWNOBSERVER_H
diff --git a/include/storage/IObbActionListener.h b/include/storage/IObbActionListener.h
new file mode 100644
index 0000000..1bedcc6
--- /dev/null
+++ b/include/storage/IObbActionListener.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IOBBACTIONLISTENER_H
+#define ANDROID_IOBBACTIONLISTENER_H
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <utils/String16.h>
+
+namespace android {
+
+class IObbActionListener: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(ObbActionListener);
+
+    virtual void onObbResult(const String16& filename, const String16& status) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnObbActionListener: public BnInterface<IObbActionListener>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IOBBACTIONLISTENER_H
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 294c867..a210880 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -94,7 +94,7 @@
     friend class SurfaceComposerClient;
 
     // camera and camcorder need access to the ISurface binder interface for preview
-    friend class Camera;
+    friend class CameraService;
     friend class MediaRecorder;
     // mediaplayer needs access to ISurface for display
     friend class MediaPlayer;
@@ -173,11 +173,12 @@
      * (eventually this should go away and be replaced by proper APIs)
      */
     // camera and camcorder need access to the ISurface binder interface for preview
-    friend class Camera;
+    friend class CameraService;
     friend class MediaRecorder;
     // MediaPlayer needs access to ISurface for display
     friend class MediaPlayer;
     friend class IOMX;
+    friend class SoftwareRenderer;
     // this is just to be able to write some unit tests
     friend class Test;
 
@@ -216,6 +217,7 @@
     int  dispatch_crop(va_list args);
     int  dispatch_set_buffer_count(va_list args);
     int  dispatch_set_buffers_geometry(va_list args);
+    int  dispatch_set_buffers_transform(va_list args);
     
     void setUsage(uint32_t reqUsage);
     int  connect(int api);
@@ -223,6 +225,7 @@
     int  crop(Rect const* rect);
     int  setBufferCount(int bufferCount);
     int  setBuffersGeometry(int w, int h, int format);
+    int  setBuffersTransform(int transform);
 
     /*
      *  private stuff...
@@ -278,6 +281,7 @@
     Rect                        mSwapRectangle;
     int                         mConnected;
     Rect                        mNextBufferCrop;
+    uint32_t                    mNextBufferTransform;
     BufferInfo                  mBufferInfo;
     
     // protected by mSurfaceLock. These are also used from lock/unlock
@@ -309,4 +313,3 @@
 }; // namespace android
 
 #endif // ANDROID_SF_SURFACE_H
-
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 2505cb0..aed4fa1 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -159,6 +159,12 @@
     virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid,
             Vector<InputTarget>& outTargets) = 0;
+
+    /* Gets the maximum suggested event delivery rate per second.
+     * This value is used to throttle motion event movement actions on a per-device
+     * basis.  It is not intended to be a hard limit.
+     */
+    virtual int32_t getMaxEventsPerSecond() = 0;
 };
 
 
@@ -332,6 +338,8 @@
         // Linked list of motion samples associated with this motion event.
         MotionSample firstSample;
         MotionSample* lastSample;
+
+        uint32_t countSamples() const;
     };
 
     // Tracks the progress of dispatching a particular event to a particular connection.
@@ -587,6 +595,17 @@
     Condition mInjectionSyncFinishedCondition;
     void decrementPendingSyncDispatchesLocked(EventEntry* entry);
 
+    // Throttling state.
+    struct ThrottleState {
+        nsecs_t minTimeBetweenEvents;
+
+        nsecs_t lastEventTime;
+        int32_t lastDeviceId;
+        uint32_t lastSource;
+
+        uint32_t originalSampleCount; // only collected during debugging
+    } mThrottleState;
+
     // Key repeat tracking.
     // XXX Move this up to the input reader instead.
     struct KeyRepeatState {
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index ca89b06..d59d72b 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -85,6 +85,7 @@
     NATIVE_WINDOW_SET_CROP,
     NATIVE_WINDOW_SET_BUFFER_COUNT,
     NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
+    NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
 };
 
 /* parameter for NATIVE_WINDOW_[DIS]CONNECT */
@@ -92,6 +93,20 @@
     NATIVE_WINDOW_API_EGL = 1
 };
 
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
+enum {
+    /* flip source image horizontally */
+    NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
+    /* flip source image vertically */
+    NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
+    /* rotate source image 90 degrees clock-wise */
+    NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
+    /* rotate source image 180 degrees */
+    NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
+    /* rotate source image 270 degrees clock-wise */
+    NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
+};
+
 struct ANativeWindow 
 {
 #ifdef __cplusplus
@@ -196,6 +211,7 @@
      *     NATIVE_WINDOW_SET_CROP
      *     NATIVE_WINDOW_SET_BUFFER_COUNT
      *     NATIVE_WINDOW_SET_BUFFERS_GEOMETRY
+     *     NATIVE_WINDOW_SET_BUFFERS_TRANSFORM
      *  
      */
     
@@ -298,6 +314,19 @@
             w, h, format);
 }
 
+/*
+ * native_window_set_buffers_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified.
+ */
+static inline int native_window_set_buffers_transform(
+        ANativeWindow* window,
+        int transform)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
+            transform);
+}
+
 // ---------------------------------------------------------------------------
 
 /* FIXME: this is legacy for pixmaps */
diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h
index d2ca82e..5243f50 100644
--- a/include/utils/ObbFile.h
+++ b/include/utils/ObbFile.h
@@ -18,12 +18,16 @@
 #define OBBFILE_H_
 
 #include <stdint.h>
+#include <strings.h>
 
 #include <utils/RefBase.h>
 #include <utils/String8.h>
 
 namespace android {
 
+// OBB flags (bit 0)
+#define OBB_OVERLAY         (1 << 0)
+
 class ObbFile : public RefBase {
 protected:
     virtual ~ObbFile();
@@ -46,18 +50,38 @@
         return mPackageName;
     }
 
-    int32_t getVersion() const {
-        return mVersion;
-    }
-
     void setPackageName(String8 packageName) {
         mPackageName = packageName;
     }
 
+    int32_t getVersion() const {
+        return mVersion;
+    }
+
     void setVersion(int32_t version) {
         mVersion = version;
     }
 
+    int32_t getFlags() const {
+        return mFlags;
+    }
+
+    void setFlags(int32_t flags) {
+        mFlags = flags;
+    }
+
+    bool isOverlay() {
+        return (mFlags & OBB_OVERLAY) == OBB_OVERLAY;
+    }
+
+    void setOverlay(bool overlay) {
+        if (overlay) {
+            mFlags |= OBB_OVERLAY;
+        } else {
+            mFlags &= ~OBB_OVERLAY;
+        }
+    }
+
     static inline uint32_t get4LE(const unsigned char* buf) {
         return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
     }
@@ -76,6 +100,9 @@
     /* Package version this ObbFile is associated with */
     int32_t mVersion;
 
+    /* Flags for this OBB type. */
+    int32_t mFlags;
+
     const char* mFileName;
 
     size_t mFileSize;
diff --git a/libs/camera/Camera.cpp b/libs/camera/Camera.cpp
index 7efc6d7..b5f78e8 100644
--- a/libs/camera/Camera.cpp
+++ b/libs/camera/Camera.cpp
@@ -167,32 +167,20 @@
     return c->unlock();
 }
 
-// pass the buffered ISurface to the camera service
+// pass the buffered Surface to the camera service
 status_t Camera::setPreviewDisplay(const sp<Surface>& surface)
 {
-    LOGV("setPreviewDisplay");
+    LOGV("setPreviewDisplay(%p)", surface.get());
     sp <ICamera> c = mCamera;
     if (c == 0) return NO_INIT;
     if (surface != 0) {
-        return c->setPreviewDisplay(surface->getISurface());
+        return c->setPreviewDisplay(surface);
     } else {
         LOGD("app passed NULL surface");
         return c->setPreviewDisplay(0);
     }
 }
 
-status_t Camera::setPreviewDisplay(const sp<ISurface>& surface)
-{
-    LOGV("setPreviewDisplay");
-    if (surface == 0) {
-        LOGD("app passed NULL surface");
-    }
-    sp <ICamera> c = mCamera;
-    if (c == 0) return NO_INIT;
-    return c->setPreviewDisplay(surface);
-}
-
-
 // start preview mode
 status_t Camera::startPreview()
 {
@@ -375,4 +363,3 @@
 }
 
 }; // namespace android
-
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index abd418a..d0ed7df 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -30,6 +30,8 @@
 const char CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS[] = "preview-format-values";
 const char CameraParameters::KEY_PREVIEW_FRAME_RATE[] = "preview-frame-rate";
 const char CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES[] = "preview-frame-rate-values";
+const char CameraParameters::KEY_PREVIEW_FPS_RANGE[] = "preview-fps-range";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE[] = "preview-fps-range-values";
 const char CameraParameters::KEY_PICTURE_SIZE[] = "picture-size";
 const char CameraParameters::KEY_SUPPORTED_PICTURE_SIZES[] = "picture-size-values";
 const char CameraParameters::KEY_PICTURE_FORMAT[] = "picture-format";
@@ -269,20 +271,24 @@
     mMap.removeItem(String8(key));
 }
 
-static int parse_size(const char *str, int &width, int &height, char **endptr = NULL)
+// Parse string like "640x480" or "10000,20000"
+static int parse_pair(const char *str, int *first, int *second, char delim,
+                      char **endptr = NULL)
 {
-    // Find the width.
+    // Find the first integer.
     char *end;
     int w = (int)strtol(str, &end, 10);
-    // If an 'x' does not immediately follow, give up.
-    if (*end != 'x')
+    // If a delimeter does not immediately follow, give up.
+    if (*end != delim) {
+        LOGE("Cannot find delimeter (%c) in str=%s", delim, str);
         return -1;
+    }
 
-    // Find the height, immediately after the 'x'.
+    // Find the second integer, immediately after the delimeter.
     int h = (int)strtol(end+1, &end, 10);
 
-    width = w;
-    height = h;
+    *first = w;
+    *second = h;
 
     if (endptr) {
         *endptr = end;
@@ -301,7 +307,8 @@
 
     while (true) {
         int width, height;
-        int success = parse_size(sizeStartPtr, width, height, &sizeStartPtr);
+        int success = parse_pair(sizeStartPtr, &width, &height, 'x',
+                                 &sizeStartPtr);
         if (success == -1 || (*sizeStartPtr != ',' && *sizeStartPtr != '\0')) {
             LOGE("Picture sizes string \"%s\" contains invalid character.", sizesStr);
             return;
@@ -324,19 +331,11 @@
 
 void CameraParameters::getPreviewSize(int *width, int *height) const
 {
-    *width = -1;
-    *height = -1;
-
+    *width = *height = -1;
     // Get the current string, if it doesn't exist, leave the -1x-1
     const char *p = get(KEY_PREVIEW_SIZE);
-    if (p == 0)
-        return;
-
-    int w, h;
-    if (parse_size(p, w, h) == 0) {
-        *width = w;
-        *height = h;
-    }
+    if (p == 0)  return;
+    parse_pair(p, width, height, 'x');
 }
 
 void CameraParameters::getSupportedPreviewSizes(Vector<Size> &sizes) const
@@ -355,6 +354,14 @@
     return getInt(KEY_PREVIEW_FRAME_RATE);
 }
 
+void CameraParameters::getPreviewFpsRange(int *min_fps, int *max_fps) const
+{
+    *min_fps = *max_fps = -1;
+    const char *p = get(KEY_PREVIEW_FPS_RANGE);
+    if (p == 0) return;
+    parse_pair(p, min_fps, max_fps, ',');
+}
+
 void CameraParameters::setPreviewFormat(const char *format)
 {
     set(KEY_PREVIEW_FORMAT, format);
@@ -374,19 +381,11 @@
 
 void CameraParameters::getPictureSize(int *width, int *height) const
 {
-    *width = -1;
-    *height = -1;
-
+    *width = *height = -1;
     // Get the current string, if it doesn't exist, leave the -1x-1
     const char *p = get(KEY_PICTURE_SIZE);
-    if (p == 0)
-        return;
-
-    int w, h;
-    if (parse_size(p, w, h) == 0) {
-        *width = w;
-        *height = h;
-    }
+    if (p == 0) return;
+    parse_pair(p, width, height, 'x');
 }
 
 void CameraParameters::getSupportedPictureSizes(Vector<Size> &sizes) const
diff --git a/libs/camera/ICamera.cpp b/libs/camera/ICamera.cpp
index 13673b5..94dc5c1 100644
--- a/libs/camera/ICamera.cpp
+++ b/libs/camera/ICamera.cpp
@@ -64,13 +64,13 @@
         remote()->transact(DISCONNECT, data, &reply);
     }
 
-    // pass the buffered ISurface to the camera service
-    status_t setPreviewDisplay(const sp<ISurface>& surface)
+    // pass the buffered Surface to the camera service
+    status_t setPreviewDisplay(const sp<Surface>& surface)
     {
         LOGV("setPreviewDisplay");
         Parcel data, reply;
         data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
-        data.writeStrongBinder(surface->asBinder());
+        Surface::writeToParcel(surface, &data);
         remote()->transact(SET_PREVIEW_DISPLAY, data, &reply);
         return reply.readInt32();
     }
@@ -258,7 +258,7 @@
         case SET_PREVIEW_DISPLAY: {
             LOGV("SET_PREVIEW_DISPLAY");
             CHECK_INTERFACE(ICamera, data, reply);
-            sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder());
+            sp<Surface> surface = Surface::readFromParcel(data);
             reply->writeInt32(setPreviewDisplay(surface));
             return NO_ERROR;
         } break;
@@ -376,4 +376,3 @@
 // ----------------------------------------------------------------------------
 
 }; // namespace android
-
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 8ed3d7b..0469508 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -6,6 +6,7 @@
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_SRC_FILES:= \
 		FontRenderer.cpp \
+		GammaFontRenderer.cpp \
 		GradientCache.cpp \
 		LayerCache.cpp \
 		Matrix.cpp \
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
new file mode 100644
index 0000000..fda57b8
--- /dev/null
+++ b/libs/hwui/Caches.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_CACHES_H
+#define ANDROID_UI_CACHES_H
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Singleton.h>
+
+#include "TextureCache.h"
+#include "LayerCache.h"
+#include "GradientCache.h"
+#include "PatchCache.h"
+#include "GammaFontRenderer.h"
+#include "ProgramCache.h"
+#include "PathCache.h"
+#include "TextDropShadowCache.h"
+
+namespace android {
+namespace uirenderer {
+
+struct CacheLogger {
+    CacheLogger() {
+        LOGD("Creating caches");
+    }
+}; // struct CacheLogger
+
+class Caches: public Singleton<Caches> {
+    Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO),
+            lastDstMode(GL_ZERO), currentProgram(NULL) {
+    }
+
+    friend class Singleton<Caches>;
+
+    CacheLogger logger;
+
+public:
+    bool blend;
+    GLenum lastSrcMode;
+    GLenum lastDstMode;
+    Program* currentProgram;
+
+    TextureCache textureCache;
+    LayerCache layerCache;
+    GradientCache gradientCache;
+    ProgramCache programCache;
+    PathCache pathCache;
+    PatchCache patchCache;
+    TextDropShadowCache dropShadowCache;
+    GammaFontRenderer fontRenderer;
+}; // class Caches
+
+}; // namespace uirenderer
+
+#ifdef USE_OPENGL_RENDERER
+using namespace uirenderer;
+ANDROID_SINGLETON_STATIC_INSTANCE(Caches);
+#endif
+
+}; // namespace android
+
+#endif // ANDROID_UI_CACHES_H
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index ccc92eb..5d7f8bf 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -101,8 +101,8 @@
             nPenX, nPenY - height, 0, u1, v1);
 }
 
-void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
-        uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
     int nPenX = x + glyph->mBitmapLeft;
     int nPenY = y + glyph->mBitmapTop;
 
@@ -116,7 +116,7 @@
     int32_t bX = 0, bY = 0;
     for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
         for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
-            if (bX < 0 || bY < 0 || bX >= (int32_t)bitmapW || bY >= (int32_t)bitmapH) {
+            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
                 LOGE("Skipping invalid index");
                 continue;
             }
@@ -286,6 +286,7 @@
 FontRenderer::FontRenderer() {
     LOGD("Creating FontRenderer");
 
+    mGammaTable = NULL;
     mInitialized = false;
     mMaxNumberOfQuads = 1024;
     mCurrentQuadIndex = 0;
@@ -405,7 +406,7 @@
     for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
         for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
             uint8_t tempCol = bitmapBuffer[bY * stride + bX];
-            cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
+            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
         }
     }
 
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 96c92d5..a03ea92 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -124,6 +124,10 @@
     void init();
     void deinit();
 
+    void setGammaTable(const uint8_t* gammaTable) {
+        mGammaTable = gammaTable;
+    }
+
     void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
     void renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
             uint32_t len, int numGlyphs, int x, int y);
@@ -157,6 +161,8 @@
 protected:
     friend class Font;
 
+    const uint8_t* mGammaTable;
+
     struct CacheTextureLine {
         uint16_t mMaxHeight;
         uint16_t mMaxWidth;
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
new file mode 100644
index 0000000..6d087e3
--- /dev/null
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "GammaFontRenderer.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+GammaFontRenderer::GammaFontRenderer() {
+    LOGD("Creating gamma font renderer");
+
+    // Get the renderer properties
+    char property[PROPERTY_VALUE_MAX];
+
+    // Get the gamma
+    float gamma = DEFAULT_TEXT_GAMMA;
+    if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
+        LOGD("  Setting text gamma to %s", property);
+        gamma = atof(property);
+    } else {
+        LOGD("  Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
+    }
+
+    // Get the black gamma threshold
+    mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
+    if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
+        LOGD("  Setting text black gamma threshold to %s", property);
+        mBlackThreshold = atoi(property);
+    } else {
+        LOGD("  Using default text black gamma threshold of %d",
+                DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD);
+    }
+
+    // Get the white gamma threshold
+    mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
+    if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
+        LOGD("  Setting text white gamma threshold to %s", property);
+        mWhiteThreshold = atoi(property);
+    } else {
+        LOGD("  Using default white black gamma threshold of %d",
+                DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
+    }
+
+    // Compute the gamma tables
+    const float blackGamma = gamma;
+    const float whiteGamma = 1.0f / gamma;
+
+    for (uint32_t i = 0; i <= 255; i++) {
+        mDefault[i] = i;
+
+        const float v = i / 255.0f;
+        const float black = pow(v, blackGamma);
+        const float white = pow(v, whiteGamma);
+
+        mBlackGamma[i] = uint8_t((float)::floor(black * 255.0f + 0.5f));
+        mWhiteGamma[i] = uint8_t((float)::floor(white * 255.0f + 0.5f));
+    }
+
+    // Configure the font renderers
+    mDefaultRenderer.setGammaTable(&mDefault[0]);
+    mBlackGammaRenderer.setGammaTable(&mBlackGamma[0]);
+    mWhiteGammaRenderer.setGammaTable(&mWhiteGamma[0]);
+}
+
+FontRenderer& GammaFontRenderer::getFontRenderer(const SkPaint* paint) {
+    if (paint->getShader() == NULL) {
+        uint32_t c = paint->getColor();
+        const int r = (c >> 16) & 0xFF;
+        const int g = (c >>  8) & 0xFF;
+        const int b = (c      ) & 0xFF;
+        const int luminance = (r * 2 + g * 5 + b) >> 3;
+
+        if (luminance <= mBlackThreshold) {
+            return mBlackGammaRenderer;
+        } else if (luminance >= mWhiteThreshold) {
+            return mWhiteGammaRenderer;
+        }
+    }
+    return mDefaultRenderer;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
new file mode 100644
index 0000000..5fa45cf
--- /dev/null
+++ b/libs/hwui/GammaFontRenderer.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_GAMMA_FONT_RENDERER_H
+#define ANDROID_UI_GAMMA_FONT_RENDERER_H
+
+#include <SkPaint.h>
+
+#include "FontRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+struct GammaFontRenderer {
+    GammaFontRenderer();
+
+    FontRenderer& getFontRenderer(const SkPaint* paint);
+
+private:
+    FontRenderer mDefaultRenderer;
+    FontRenderer mBlackGammaRenderer;
+    FontRenderer mWhiteGammaRenderer;
+
+    int mBlackThreshold;
+    int mWhiteThreshold;
+
+    uint8_t mDefault[256];
+    uint8_t mBlackGamma[256];
+    uint8_t mWhiteGamma[256];
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_GAMMA_FONT_RENDERER_H
diff --git a/libs/hwui/GenerationCache.h b/libs/hwui/GenerationCache.h
index 45b3ffa..c358c80 100644
--- a/libs/hwui/GenerationCache.h
+++ b/libs/hwui/GenerationCache.h
@@ -104,12 +104,14 @@
 template<typename K, typename V>
 void GenerationCache<K, V>::clear() {
     if (mListener) {
-        while (mCache.size() > 0) {
-            removeOldest();
+        for (uint32_t i = 0; i < mCache.size(); i++) {
+            sp<Entry<K, V> > entry = mCache.valueAt(i);
+            if (mListener) {
+                (*mListener)(entry->key, entry->value);
+            }
         }
-    } else {
-        mCache.clear();
     }
+    mCache.clear();
     mYoungest.clear();
     mOldest.clear();
 }
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 59fa0a7..58920bd 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -22,6 +22,7 @@
 #include <SkGradientShader.h>
 
 #include "GradientCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -30,6 +31,20 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+GradientCache::GradientCache():
+        mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting gradient cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
+    }
+
+    mCache.setOnEntryRemovedListener(this);
+}
+
 GradientCache::GradientCache(uint32_t maxByteSize):
         mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 12c8a23..8795920 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -32,6 +32,7 @@
  */
 class GradientCache: public OnEntryRemoved<SkShader*, Texture*> {
 public:
+    GradientCache();
     GradientCache(uint32_t maxByteSize);
     ~GradientCache();
 
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 3d263a3..a204778 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -21,6 +21,7 @@
 #include <utils/Log.h>
 
 #include "LayerCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -29,6 +30,18 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+LayerCache::LayerCache():
+        mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity),
+        mIdGenerator(1), mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting layer cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE);
+    }
+}
+
 LayerCache::LayerCache(uint32_t maxByteSize):
         mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity),
         mIdGenerator(1), mSize(0), mMaxSize(maxByteSize) {
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 2580551..9860994 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -43,6 +43,7 @@
 
 class LayerCache: public OnEntryRemoved<LayerSize, Layer*> {
 public:
+    LayerCache();
     LayerCache(uint32_t maxByteSize);
     ~LayerCache();
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3440687..4ce30b0 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -23,11 +23,9 @@
 #include <SkCanvas.h>
 #include <SkTypeface.h>
 
-#include <cutils/properties.h>
 #include <utils/Log.h>
 
 #include "OpenGLRenderer.h"
-#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -36,18 +34,8 @@
 // Defines
 ///////////////////////////////////////////////////////////////////////////////
 
-#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
-#define DEFAULT_LAYER_CACHE_SIZE 6.0f
-#define DEFAULT_PATH_CACHE_SIZE 6.0f
-#define DEFAULT_PATCH_CACHE_SIZE 100
-#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
-#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
-
 #define REQUIRED_TEXTURE_UNITS_COUNT 3
 
-// Converts a number of mega-bytes into bytes
-#define MB(s) s * 1024 * 1024
-
 // Generates simple and textured vertices
 #define FV(x, y, u, v) { { x, y }, { u, v } }
 
@@ -102,54 +90,9 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-OpenGLRenderer::OpenGLRenderer():
-        mBlend(false), mLastSrcMode(GL_ZERO), mLastDstMode(GL_ZERO),
-        mTextureCache(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
-        mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)),
-        mGradientCache(MB(DEFAULT_GRADIENT_CACHE_SIZE)),
-        mPathCache(MB(DEFAULT_PATH_CACHE_SIZE)),
-        mPatchCache(DEFAULT_PATCH_CACHE_SIZE),
-        mDropShadowCache(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) {
+OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) {
     LOGD("Create OpenGLRenderer");
 
-    char property[PROPERTY_VALUE_MAX];
-    if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
-        LOGD("  Setting texture cache size to %sMB", property);
-        mTextureCache.setMaxSize(MB(atof(property)));
-    } else {
-        LOGD("  Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
-    }
-
-    if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) {
-        LOGD("  Setting layer cache size to %sMB", property);
-        mLayerCache.setMaxSize(MB(atof(property)));
-    } else {
-        LOGD("  Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE);
-    }
-
-    if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
-        LOGD("  Setting gradient cache size to %sMB", property);
-        mGradientCache.setMaxSize(MB(atof(property)));
-    } else {
-        LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
-    }
-
-    if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) {
-        LOGD("  Setting path cache size to %sMB", property);
-        mPathCache.setMaxSize(MB(atof(property)));
-    } else {
-        LOGD("  Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE);
-    }
-
-    if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) {
-        LOGD("  Setting drop shadow cache size to %sMB", property);
-        mDropShadowCache.setMaxSize(MB(atof(property)));
-    } else {
-        LOGD("  Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE);
-    }
-    mDropShadowCache.setFontRenderer(mFontRenderer);
-
-    mCurrentProgram = NULL;
     mShader = NULL;
     mColorFilter = NULL;
     mHasShadow = false;
@@ -167,14 +110,6 @@
 
 OpenGLRenderer::~OpenGLRenderer() {
     LOGD("Destroy OpenGLRenderer");
-
-    mTextureCache.clear();
-    mLayerCache.clear();
-    mGradientCache.clear();
-    mPathCache.clear();
-    mPatchCache.clear();
-    mProgramCache.clear();
-    mDropShadowCache.clear();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -195,6 +130,8 @@
     mSnapshot = new Snapshot(mFirstSnapshot);
     mSaveCount = 1;
 
+    glViewport(0, 0, mWidth, mHeight);
+
     glDisable(GL_SCISSOR_TEST);
 
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@@ -310,12 +247,12 @@
 bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
         float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
     LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
-    LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize());
+    LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
 
     GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
     LayerSize size(right - left, bottom - top);
 
-    Layer* layer = mLayerCache.get(size, previousFbo);
+    Layer* layer = mCaches.layerCache.get(size, previousFbo);
     if (!layer) {
         return false;
     }
@@ -380,7 +317,7 @@
     LayerSize size(rect.getWidth(), rect.getHeight());
     // Failing to add the layer to the cache should happen only if the
     // layer is too large
-    if (!mLayerCache.put(size, layer)) {
+    if (!mCaches.layerCache.put(size, layer)) {
         LAYER_LOGD("Deleting layer");
 
         glDeleteFramebuffers(1, &layer->fbo);
@@ -461,7 +398,7 @@
     }
 
     glActiveTexture(GL_TEXTURE0);
-    const Texture* texture = mTextureCache.get(bitmap);
+    const Texture* texture = mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
@@ -478,7 +415,7 @@
     }
 
     glActiveTexture(GL_TEXTURE0);
-    const Texture* texture = mTextureCache.get(bitmap);
+    const Texture* texture = mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
@@ -494,7 +431,7 @@
     }
 
     glActiveTexture(GL_TEXTURE0);
-    const Texture* texture = mTextureCache.get(bitmap);
+    const Texture* texture = mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
@@ -520,7 +457,7 @@
     }
 
     glActiveTexture(GL_TEXTURE0);
-    const Texture* texture = mTextureCache.get(bitmap);
+    const Texture* texture = mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
@@ -528,7 +465,7 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
-    Patch* mesh = mPatchCache.get(patch);
+    Patch* mesh = mCaches.patchCache.get(patch);
     mesh->updateVertices(bitmap, left, top, right, bottom,
             &patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs);
 
@@ -575,6 +512,14 @@
         return;
     }
 
+    float scaleX = paint->getTextScaleX();
+    bool applyScaleX = scaleX < 0.9999f || scaleX > 1.0001f;
+    if (applyScaleX) {
+        save(0);
+        translate(x - (x * scaleX), 0.0f);
+        scale(scaleX, 1.0f);
+    }
+
     float length = -1.0f;
     switch (paint->getTextAlign()) {
         case SkPaint::kCenter_Align:
@@ -599,10 +544,13 @@
     const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
     const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
 
-    mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
+    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
+    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
+            paint->getTextSize());
     if (mHasShadow) {
         glActiveTexture(gTextureUnits[0]);
-        const ShadowTexture* shadow = mDropShadowCache.get(paint, text, bytesCount,
+        mCaches.dropShadowCache.setFontRenderer(fontRenderer);
+        const ShadowTexture* shadow = mCaches.dropShadowCache.get(paint, text, bytesCount,
                 count, mShadowRadius);
         const AutoTexture autoCleanup(shadow);
 
@@ -610,22 +558,26 @@
 
         // Draw the mesh
         glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-        glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
+        glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
     }
 
     GLuint textureUnit = 0;
     glActiveTexture(gTextureUnits[textureUnit]);
 
-    setupTextureAlpha8(mFontRenderer.getTexture(), 0, 0, textureUnit, x, y, r, g, b, a,
+    setupTextureAlpha8(fontRenderer.getTexture(), 0, 0, textureUnit, x, y, r, g, b, a,
             mode, false, true);
 
     const Rect& clip = mSnapshot->getLocalClip();
-    mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
+    fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-    glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
+    glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
 
     drawTextDecorations(text, bytesCount, length, x, y, paint);
+
+    if (applyScaleX) {
+        restore();
+    }
 }
 
 void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
@@ -634,7 +586,7 @@
     GLuint textureUnit = 0;
     glActiveTexture(gTextureUnits[textureUnit]);
 
-    const PathTexture* texture = mPathCache.get(path, paint);
+    const PathTexture* texture = mCaches.pathCache.get(path, paint);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
@@ -659,7 +611,7 @@
 
     // Draw the mesh
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-    glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
+    glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -673,7 +625,7 @@
 void OpenGLRenderer::setupShader(SkiaShader* shader) {
     mShader = shader;
     if (mShader) {
-        mShader->set(&mTextureCache, &mGradientCache);
+        mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
     }
 }
 
@@ -749,18 +701,18 @@
      }
 
      // Build and use the appropriate shader
-     useProgram(mProgramCache.get(description));
+     useProgram(mCaches.programCache.get(description));
 
      // Setup the blending mode
      chooseBlending(true, mode);
      bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
-     glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
+     glUniform1i(mCaches.currentProgram->getUniform("sampler"), textureUnit);
 
-     int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
+     int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
      glEnableVertexAttribArray(texCoordsSlot);
 
      // Setup attributes
-     glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+     glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
              gMeshStride, &mMeshVertices[0].position[0]);
      glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
              gMeshStride, &mMeshVertices[0].texture[0]);
@@ -772,17 +724,17 @@
      } else {
          mModelView.loadIdentity();
      }
-     mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
-     glUniform4f(mCurrentProgram->color, r, g, b, a);
+     mCaches.currentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+     glUniform4f(mCaches.currentProgram->color, r, g, b, a);
 
      textureUnit++;
      if (applyFilters) {
          // Setup attributes and uniforms required by the shaders
          if (mShader) {
-             mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
+             mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit);
          }
          if (mColorFilter) {
-             mColorFilter->setupProgram(mCurrentProgram);
+             mColorFilter->setupProgram(mCaches.currentProgram);
          }
      }
 }
@@ -867,29 +819,29 @@
     }
 
     // Build and use the appropriate shader
-    useProgram(mProgramCache.get(description));
+    useProgram(mCaches.programCache.get(description));
 
     // Setup attributes
-    glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
             gMeshStride, &mMeshVertices[0].position[0]);
 
     // Setup uniforms
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
     if (!ignoreTransform) {
-        mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
     } else {
         mat4 identity;
-        mCurrentProgram->set(mOrthoMatrix, mModelView, identity);
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity);
     }
-    glUniform4f(mCurrentProgram->color, r, g, b, a);
+    glUniform4f(mCaches.currentProgram->color, r, g, b, a);
 
     // Setup attributes and uniforms required by the shaders
     if (mShader) {
-        mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
+        mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit);
     }
     if (mColorFilter) {
-        mColorFilter->setupProgram(mCurrentProgram);
+        mColorFilter->setupProgram(mCaches.currentProgram);
     }
 
     // Draw the mesh
@@ -924,28 +876,28 @@
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
 
-    useProgram(mProgramCache.get(description));
-    mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+    useProgram(mCaches.programCache.get(description));
+    mCaches.currentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
     chooseBlending(blend || alpha < 1.0f, mode);
 
     // Texture
     bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0);
-    glUniform1i(mCurrentProgram->getUniform("sampler"), 0);
+    glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0);
 
     // Always premultiplied
-    glUniform4f(mCurrentProgram->color, alpha, alpha, alpha, alpha);
+    glUniform4f(mCaches.currentProgram->color, alpha, alpha, alpha, alpha);
 
     // Mesh
-    int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
+    int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
     glEnableVertexAttribArray(texCoordsSlot);
-    glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
             gMeshStride, vertices);
     glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords);
 
     // Color filter
     if (mColorFilter) {
-        mColorFilter->setupProgram(mCurrentProgram);
+        mColorFilter->setupProgram(mCaches.currentProgram);
     }
 
     if (!indices) {
@@ -959,7 +911,7 @@
 void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied) {
     blend = blend || mode != SkXfermode::kSrcOver_Mode;
     if (blend) {
-        if (!mBlend) {
+        if (!mCaches.blend) {
             glEnable(GL_BLEND);
         }
 
@@ -969,22 +921,22 @@
             sourceMode = GL_SRC_ALPHA;
         }
 
-        if (sourceMode != mLastSrcMode || destMode != mLastDstMode) {
+        if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
             glBlendFunc(sourceMode, destMode);
-            mLastSrcMode = sourceMode;
-            mLastDstMode = destMode;
+            mCaches.lastSrcMode = sourceMode;
+            mCaches.lastDstMode = destMode;
         }
-    } else if (mBlend) {
+    } else if (mCaches.blend) {
         glDisable(GL_BLEND);
     }
-    mBlend = blend;
+    mCaches.blend = blend;
 }
 
 bool OpenGLRenderer::useProgram(Program* program) {
     if (!program->isInUse()) {
-        if (mCurrentProgram != NULL) mCurrentProgram->remove();
+        if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove();
         program->use();
-        mCurrentProgram = program;
+        mCaches.currentProgram = program;
         return false;
     }
     return true;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 5c0089f..5748d57 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -35,17 +35,10 @@
 #include "Program.h"
 #include "Rect.h"
 #include "Snapshot.h"
-#include "TextureCache.h"
-#include "LayerCache.h"
-#include "GradientCache.h"
-#include "PatchCache.h"
 #include "Vertex.h"
-#include "FontRenderer.h"
-#include "ProgramCache.h"
 #include "SkiaShader.h"
 #include "SkiaColorFilter.h"
-#include "PathCache.h"
-#include "TextDropShadowCache.h"
+#include "Caches.h"
 
 namespace android {
 namespace uirenderer {
@@ -356,7 +349,6 @@
     sp<Snapshot> mSnapshot;
 
     // Shaders
-    Program* mCurrentProgram;
     SkiaShader* mShader;
 
     // Color filters
@@ -365,17 +357,9 @@
     // Used to draw textured quads
     TextureVertex mMeshVertices[4];
 
-    // Last known blend state
-    bool mBlend;
-    GLenum mLastSrcMode;
-    GLenum mLastDstMode;
-
     // GL extensions
     Extensions mExtensions;
 
-    // Font renderer
-    FontRenderer mFontRenderer;
-
     // Drop shadow
     bool mHasShadow;
     float mShadowRadius;
@@ -384,13 +368,7 @@
     int mShadowColor;
 
     // Various caches
-    TextureCache mTextureCache;
-    LayerCache mLayerCache;
-    GradientCache mGradientCache;
-    ProgramCache mProgramCache;
-    PathCache mPathCache;
-    PatchCache mPatchCache;
-    TextDropShadowCache mDropShadowCache;
+    Caches& mCaches;
 }; // class OpenGLRenderer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 694e3fd..a7c0cce 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -20,6 +20,7 @@
 #include <utils/ResourceTypes.h>
 
 #include "PatchCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -28,6 +29,9 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+PatchCache::PatchCache(): mCache(DEFAULT_PATCH_CACHE_SIZE) {
+}
+
 PatchCache::PatchCache(uint32_t maxEntries): mCache(maxEntries) {
 }
 
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index de95087..e874a16 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -43,6 +43,7 @@
 
 class PatchCache: public OnEntryRemoved<PatchDescription, Patch*> {
 public:
+    PatchCache();
     PatchCache(uint32_t maxCapacity);
     ~PatchCache();
 
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 10440ea..158c0cc 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -22,6 +22,7 @@
 #include <SkRect.h>
 
 #include "PathCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -30,9 +31,30 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+PathCache::PathCache():
+        mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting path cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE);
+    }
+    init();
+}
+
 PathCache::PathCache(uint32_t maxByteSize):
         mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
+    init();
+}
+
+PathCache::~PathCache() {
+    mCache.clear();
+}
+
+void PathCache::init() {
     mCache.setOnEntryRemovedListener(this);
 
     GLint maxTextureSize;
@@ -40,10 +62,6 @@
     mMaxTextureSize = maxTextureSize;
 }
 
-PathCache::~PathCache() {
-    mCache.clear();
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Size management
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index d09789f..522e5e0 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -95,6 +95,7 @@
  */
 class PathCache: public OnEntryRemoved<PathCacheEntry, PathTexture*> {
 public:
+    PathCache();
     PathCache(uint32_t maxByteSize);
     ~PathCache();
 
@@ -135,6 +136,8 @@
 
     PathTexture* addTexture(const PathCacheEntry& entry, const SkPath *path, const SkPaint* paint);
 
+    void init();
+
     GenerationCache<PathCacheEntry, PathTexture*> mCache;
 
     uint32_t mSize;
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index fa4b8c4..f211ab6 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -183,7 +183,6 @@
     void printLongString(const String8& shader) const;
 
     KeyedVector<programid, Program*> mCache;
-
 }; // class ProgramCache
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 7514b6f..4e2f091 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_UI_PROPERTIES_H
 #define ANDROID_UI_PROPERTIES_H
 
+#include <cutils/properties.h>
+
 /**
  * This file contains the list of system properties used to configure
  * the OpenGLRenderer.
@@ -33,4 +35,23 @@
 #define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width"
 #define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height"
 
+// Gamma (>= 1.0, <= 10.0)
+#define PROPERTY_TEXT_GAMMA "ro.text_gamma"
+#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "ro.text_gamma.black_threshold"
+#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "ro.text_gamma.white_threshold"
+
+// Converts a number of mega-bytes into bytes
+#define MB(s) s * 1024 * 1024
+
+#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
+#define DEFAULT_LAYER_CACHE_SIZE 6.0f
+#define DEFAULT_PATH_CACHE_SIZE 6.0f
+#define DEFAULT_PATCH_CACHE_SIZE 100
+#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
+#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
+
+#define DEFAULT_TEXT_GAMMA 1.4f
+#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
+#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
+
 #endif // ANDROID_UI_PROPERTIES_H
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index aab5bd4..9d54277 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "OpenGLRenderer"
 
 #include "TextDropShadowCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -25,6 +26,20 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+TextDropShadowCache::TextDropShadowCache():
+        mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting drop shadow cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE);
+    }
+
+    mCache.setOnEntryRemovedListener(this);
+}
+
 TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize):
         mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index c3be483..b65d62a 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -69,16 +69,16 @@
     char *text;
 
     bool operator<(const ShadowText& rhs) const {
-        if (len < rhs.len) return true;
-        else if (len == rhs.len) {
-            if (radius < rhs.radius) return true;
-            else if (radius == rhs.radius) {
-                if (textSize < rhs.textSize) return true;
-                else if (textSize == rhs.textSize) {
-                    if (typeface < rhs.typeface) return true;
-                    else if (typeface == rhs.typeface) {
-                        if (hash < rhs.hash) return true;
-                        if (hash == rhs.hash) {
+        if (hash < rhs.hash) return true;
+        else if (hash == rhs.hash) {
+            if (len < rhs.len) return true;
+            else if (len == rhs.len) {
+                if (radius < rhs.radius) return true;
+                else if (radius == rhs.radius) {
+                    if (textSize < rhs.textSize) return true;
+                    else if (textSize == rhs.textSize) {
+                        if (typeface < rhs.typeface) return true;
+                        else if (typeface == rhs.typeface) {
                             return strncmp(text, rhs.text, len) < 0;
                         }
                     }
@@ -102,6 +102,7 @@
 
 class TextDropShadowCache: public OnEntryRemoved<ShadowText, ShadowTexture*> {
 public:
+    TextDropShadowCache();
     TextDropShadowCache(uint32_t maxByteSize);
     ~TextDropShadowCache();
 
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 2e8a8be..753c544 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -19,6 +19,7 @@
 #include <GLES2/gl2.h>
 
 #include "TextureCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -27,19 +28,37 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+TextureCache::TextureCache():
+        mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting texture cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
+    }
+
+    init();
+}
+
 TextureCache::TextureCache(uint32_t maxByteSize):
         mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
-    mCache.setOnEntryRemovedListener(this);
-
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
-    LOGD("Maximum texture dimension is %d pixels", mMaxTextureSize);
+    init();
 }
 
 TextureCache::~TextureCache() {
     mCache.clear();
 }
 
+void TextureCache::init() {
+    mCache.setOnEntryRemovedListener(this);
+
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+    LOGD("    Maximum texture dimension is %d pixels", mMaxTextureSize);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Size management
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 452716c..b5e4c7c 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -32,6 +32,7 @@
  */
 class TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> {
 public:
+    TextureCache();
     TextureCache(uint32_t maxByteSize);
     ~TextureCache();
 
@@ -78,6 +79,8 @@
      */
     void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
 
+    void init();
+
     GenerationCache<SkBitmap*, Texture*> mCache;
 
     uint32_t mSize;
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 3406287..7902b9a 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -91,7 +91,11 @@
     RS_TYPE_UNSIGNED_5_5_5_1,
     RS_TYPE_UNSIGNED_4_4_4_4,
 
-    RS_TYPE_ELEMENT,
+    RS_TYPE_MATRIX_4X4,
+    RS_TYPE_MATRIX_3X3,
+    RS_TYPE_MATRIX_2X2,
+
+    RS_TYPE_ELEMENT = 1000,
     RS_TYPE_TYPE,
     RS_TYPE_ALLOCATION,
     RS_TYPE_SAMPLER,
@@ -100,7 +104,7 @@
     RS_TYPE_PROGRAM_FRAGMENT,
     RS_TYPE_PROGRAM_VERTEX,
     RS_TYPE_PROGRAM_RASTER,
-    RS_TYPE_PROGRAM_STORE
+    RS_TYPE_PROGRAM_STORE,
 };
 
 enum RsDataKind {
diff --git a/libs/rs/java/ModelViewer/AndroidManifest.xml b/libs/rs/java/ModelViewer/AndroidManifest.xml
index 39976d2..959fe53 100644
--- a/libs/rs/java/ModelViewer/AndroidManifest.xml
+++ b/libs/rs/java/ModelViewer/AndroidManifest.xml
@@ -2,7 +2,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.modelviewer">
     <application android:label="ModelViewer">
-        <activity android:name="ModelViewer"
+        <activity android:name="SimpleModel"
+                  android:label="SimpleModel"
                   android:screenOrientation="portrait"
                   android:theme="@android:style/Theme.Black.NoTitleBar">
             <intent-filter>
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
index 3f4d930..9672a6a 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
@@ -90,7 +90,7 @@
     }
 
     private void initPFS() {
-        ProgramStore.Builder b = new ProgramStore.Builder(mRS, null, null);
+        ProgramStore.Builder b = new ProgramStore.Builder(mRS);
 
         b.setDepthFunc(ProgramStore.DepthFunc.LESS);
         b.setDitherEnable(false);
@@ -118,7 +118,7 @@
     }
 
     private void initPV() {
-        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
+        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS);
         mPVBackground = pvb.create();
 
         mPVA = new ProgramVertex.MatrixAllocation(mRS);
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java
index bfc9bb7..e70e811 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java
@@ -82,15 +82,8 @@
 
     void setData() {
 
-        mTransformData.globalMat_Row0 = new Float4(1, 0, 0, 0);
-        mTransformData.globalMat_Row1 = new Float4(0, 1, 0, 0);
-        mTransformData.globalMat_Row2 = new Float4(0, 0, 1, 0);
-        mTransformData.globalMat_Row3 = new Float4(0, 0, 0, 1);
-
-        mTransformData.localMat_Row0 = new Float4(1, 0, 0, 0);
-        mTransformData.localMat_Row1 = new Float4(0, 1, 0, 0);
-        mTransformData.localMat_Row2 = new Float4(0, 0, 1, 0);
-        mTransformData.localMat_Row3 = new Float4(0, 0, 0, 1);
+        mTransformData.globalMat = new Matrix4f();
+        mTransformData.localMat = new Matrix4f();
 
         mTransformData.transforms0 = mTransforms[0];
         mTransformData.transforms1 = mTransforms[1];
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewer.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java
similarity index 93%
rename from libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewer.java
rename to libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java
index 7491744..cb7c39c 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewer.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java
@@ -37,9 +37,9 @@
 
 import java.lang.Runtime;
 
-public class ModelViewer extends Activity {
+public class SimpleModel extends Activity {
 
-    private ModelViewerView mView;
+    private SimpleModelView mView;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -47,7 +47,7 @@
 
         // Create our Preview view and set it as the content of our
         // Activity
-        mView = new ModelViewerView(this);
+        mView = new SimpleModelView(this);
         setContentView(mView);
     }
 
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
similarity index 93%
rename from libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java
rename to libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
index 479aaf3..b8b0119 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
@@ -24,7 +24,7 @@
 import android.util.Log;
 
 
-public class ModelViewerRS {
+public class SimpleModelRS {
 
     private final int STATE_LAST_FOCUS = 1;
 
@@ -32,7 +32,7 @@
     int mHeight;
     int mRotation;
 
-    public ModelViewerRS() {
+    public SimpleModelRS() {
     }
 
     public void init(RenderScriptGL rs, Resources res, int width, int height) {
@@ -60,7 +60,7 @@
     private Font mItalic;
     private Allocation mTextAlloc;
 
-    private ScriptC_Modelviewer mScript;
+    private ScriptC_Simplemodel mScript;
 
     int mLastX;
     int mLastY;
@@ -86,7 +86,7 @@
     }
 
     private void initPFS() {
-        ProgramStore.Builder b = new ProgramStore.Builder(mRS, null, null);
+        ProgramStore.Builder b = new ProgramStore.Builder(mRS);
 
         b.setDepthFunc(ProgramStore.DepthFunc.LESS);
         b.setDitherEnable(false);
@@ -114,7 +114,7 @@
     }
 
     private void initPV() {
-        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
+        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS);
         mPVBackground = pvb.create();
 
         mPVA = new ProgramVertex.MatrixAllocation(mRS);
@@ -139,7 +139,7 @@
 
     private void initRS() {
 
-        mScript = new ScriptC_Modelviewer(mRS, mRes, R.raw.modelviewer, true);
+        mScript = new ScriptC_Simplemodel(mRS, mRes, R.raw.simplemodel, true);
 
         initPFS();
         initPF();
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerView.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java
similarity index 93%
rename from libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerView.java
rename to libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java
index 061cf8e..2574fdd 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerView.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java
@@ -39,15 +39,15 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
-public class ModelViewerView extends RSSurfaceView {
+public class SimpleModelView extends RSSurfaceView {
 
-    public ModelViewerView(Context context) {
+    public SimpleModelView(Context context) {
         super(context);
         //setFocusable(true);
     }
 
     private RenderScriptGL mRS;
-    private ModelViewerRS mRender;
+    private SimpleModelRS mRender;
 
 
     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
@@ -55,7 +55,7 @@
         if (mRS == null) {
             mRS = createRenderScript(true);
             mRS.contextSetSurface(w, h, holder.getSurface());
-            mRender = new ModelViewerRS();
+            mRender = new SimpleModelRS();
             mRender.init(mRS, getResources(), w, h);
         }
     }
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
index 8053306..c794438 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
@@ -76,10 +76,10 @@
     rsgBindProgramStore(gPFSBackground);
     rsgBindTexture(gPFBackground, 0, gTGrid);
 
-    rsgProgramVertexLoadModelMatrix((rs_matrix4x4 *)&robot1Ptr->globalMat_Row0);
+    rsgProgramVertexLoadModelMatrix(&robot1Ptr->globalMat);
     rsgDrawMesh(gTestMesh);
 
-    rsgProgramVertexLoadModelMatrix((rs_matrix4x4 *)&robot2Ptr->globalMat_Row0);
+    rsgProgramVertexLoadModelMatrix(&robot2Ptr->globalMat);
     rsgDrawMesh(gTestMesh);
 
     color(0.3f, 0.3f, 0.3f, 1.0f);
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/modelviewer.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs
similarity index 100%
rename from libs/rs/java/ModelViewer/src/com/android/modelviewer/modelviewer.rs
rename to libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs
index 7b9cd1c..2ef29cf 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs
@@ -52,8 +52,8 @@
     //rsDebug("Transform data", (int)data);
     //rsDebug("Entering parent", (int)parent);
 
-    rs_matrix4x4 *localMat = (rs_matrix4x4*)&data->localMat_Row0;
-    rs_matrix4x4 *globalMat = (rs_matrix4x4*)&data->globalMat_Row0;
+    rs_matrix4x4 *localMat = &data->localMat;
+    rs_matrix4x4 *globalMat = &data->globalMat;
 
     ParentData toChild;
     toChild.changed = 0;
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
index a66e6c5..5c872a7 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
@@ -22,15 +22,8 @@
 #define TRANSFORM_SCALE 3
 
 typedef struct {
-    float4 globalMat_Row0;
-    float4 globalMat_Row1;
-    float4 globalMat_Row2;
-    float4 globalMat_Row3;
-
-    float4 localMat_Row0;
-    float4 localMat_Row1;
-    float4 localMat_Row2;
-    float4 localMat_Row3;
+    rs_matrix4x4 globalMat;
+    rs_matrix4x4 localMat;
 
     float4 transforms0;
     float4 transforms1;
diff --git a/libs/rs/java/Samples/AndroidManifest.xml b/libs/rs/java/Samples/AndroidManifest.xml
index 17c6b16..be191f2 100644
--- a/libs/rs/java/Samples/AndroidManifest.xml
+++ b/libs/rs/java/Samples/AndroidManifest.xml
@@ -11,5 +11,14 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        
+        <activity android:name="RsRenderStates"
+                  android:label="RsStates"                  
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/libs/rs/java/Samples/res/drawable/data.png b/libs/rs/java/Samples/res/drawable/data.png
new file mode 100644
index 0000000..8e34714
--- /dev/null
+++ b/libs/rs/java/Samples/res/drawable/data.png
Binary files differ
diff --git a/libs/rs/java/Samples/res/drawable/leaf.png b/libs/rs/java/Samples/res/drawable/leaf.png
new file mode 100644
index 0000000..3cd3775
--- /dev/null
+++ b/libs/rs/java/Samples/res/drawable/leaf.png
Binary files differ
diff --git a/libs/rs/java/Samples/res/drawable/torusmap.png b/libs/rs/java/Samples/res/drawable/torusmap.png
new file mode 100644
index 0000000..1e08f3b
--- /dev/null
+++ b/libs/rs/java/Samples/res/drawable/torusmap.png
Binary files differ
diff --git a/libs/rs/java/Samples/res/raw/torus.a3d b/libs/rs/java/Samples/res/raw/torus.a3d
new file mode 100644
index 0000000..610f095
--- /dev/null
+++ b/libs/rs/java/Samples/res/raw/torus.a3d
Binary files differ
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewer.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStates.java
similarity index 91%
copy from libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewer.java
copy to libs/rs/java/Samples/src/com/android/samples/RsRenderStates.java
index 7491744..391007e 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewer.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStates.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.modelviewer;
+package com.android.samples;
 
 import android.renderscript.RSSurfaceView;
 import android.renderscript.RenderScript;
@@ -37,9 +37,9 @@
 
 import java.lang.Runtime;
 
-public class ModelViewer extends Activity {
+public class RsRenderStates extends Activity {
 
-    private ModelViewerView mView;
+    private RsRenderStatesView mView;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -47,7 +47,7 @@
 
         // Create our Preview view and set it as the content of our
         // Activity
-        mView = new ModelViewerView(this);
+        mView = new RsRenderStatesView(this);
         setContentView(mView);
     }
 
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
new file mode 100644
index 0000000..8eff455
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.samples;
+
+import java.io.Writer;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+
+
+public class RsRenderStatesRS {
+
+    int mWidth;
+    int mHeight;
+
+    public RsRenderStatesRS() {
+    }
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+        mWidth = width;
+        mHeight = height;
+        mOptionsARGB.inScaled = false;
+        mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        mMode = 0;
+        mMaxModes = 7;
+        initRS();
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+
+    private Sampler mLinearClamp;
+    private Sampler mLinearWrap;
+    private Sampler mMipLinearWrap;
+    private Sampler mNearestClamp;
+
+    private ProgramStore mProgStoreBlendNoneDepth;
+    private ProgramStore mProgStoreBlendNone;
+    private ProgramStore mProgStoreBlendAlpha;
+    private ProgramStore mProgStoreBlendAdd;
+
+    private ProgramFragment mProgFragmentTexture;
+    private ProgramFragment mProgFragmentColor;
+
+    private ProgramVertex mProgVertex;
+    private ProgramVertex.MatrixAllocation mPVA;
+
+    private ProgramRaster mCullBack;
+    private ProgramRaster mCullFront;
+
+    private Allocation mTexTorus;
+    private Allocation mTexOpaque;
+    private Allocation mTexTransparent;
+
+    private Allocation mAllocPV;
+
+    private Mesh mMbyNMesh;
+    private Mesh mTorus;
+
+    Font mFontSans;
+    Font mFontSerif;
+    Font mFontSerifBold;
+    Font mFontSerifItalic;
+    Font mFontSerifBoldItalic;
+    Font mFontMono;
+
+    private Allocation mTextAlloc;
+
+    private ScriptC_Rsrenderstates mScript;
+
+    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
+
+    int mMode;
+    int mMaxModes;
+
+    public void onActionDown(int x, int y) {
+        mMode ++;
+        mMode = mMode % mMaxModes;
+        mScript.set_gDisplayMode(mMode);
+    }
+
+    private Mesh getMbyNMesh(float width, float height, int wResolution, int hResolution) {
+
+        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
+                                           2, Mesh.TriangleMeshBuilder.TEXTURE_0);
+
+        for (int y = 0; y <= hResolution; y++) {
+            final float normalizedY = (float)y / hResolution;
+            final float yOffset = (normalizedY - 0.5f) * height;
+            for (int x = 0; x <= wResolution; x++) {
+                float normalizedX = (float)x / wResolution;
+                float xOffset = (normalizedX - 0.5f) * width;
+                tmb.setTexture(normalizedX, normalizedY);
+                tmb.addVertex(xOffset, yOffset);
+             }
+        }
+
+        for (int y = 0; y < hResolution; y++) {
+            final int curY = y * (wResolution + 1);
+            final int belowY = (y + 1) * (wResolution + 1);
+            for (int x = 0; x < wResolution; x++) {
+                int curV = curY + x;
+                int belowV = belowY + x;
+                tmb.addTriangle(curV, belowV, curV + 1);
+                tmb.addTriangle(belowV, belowV + 1, curV + 1);
+            }
+        }
+
+        return tmb.create(true);
+    }
+
+    private void initProgramStore() {
+        // Use stock the stock program store object
+        mProgStoreBlendNoneDepth = ProgramStore.BLEND_NONE_DEPTH_TEST(mRS);
+        mProgStoreBlendNone = ProgramStore.BLEND_NONE_DEPTH_NO_DEPTH(mRS);
+
+        // Create a custom program store
+        ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
+        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+        builder.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA,
+                             ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+        builder.setDitherEnable(false);
+        builder.setDepthMask(false);
+        mProgStoreBlendAlpha = builder.create();
+
+        mProgStoreBlendAdd = ProgramStore.BLEND_ADD_DEPTH_NO_DEPTH(mRS);
+
+        mScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth);
+        mScript.set_gProgStoreBlendNone(mProgStoreBlendNone);
+        mScript.set_gProgStoreBlendAlpha(mProgStoreBlendAlpha);
+        mScript.set_gProgStoreBlendAdd(mProgStoreBlendAdd);
+    }
+
+    private void initProgramFragment() {
+
+        ProgramFragment.Builder texBuilder = new ProgramFragment.Builder(mRS);
+        texBuilder.setTexture(ProgramFragment.Builder.EnvMode.REPLACE,
+                              ProgramFragment.Builder.Format.RGBA, 0);
+        mProgFragmentTexture = texBuilder.create();
+        mProgFragmentTexture.bindSampler(mLinearClamp, 0);
+
+        ProgramFragment.Builder colBuilder = new ProgramFragment.Builder(mRS);
+        colBuilder.setVaryingColor(false);
+        mProgFragmentColor = colBuilder.create();
+
+        mScript.set_gProgFragmentColor(mProgFragmentColor);
+        mScript.set_gProgFragmentTexture(mProgFragmentTexture);
+    }
+
+    private void initProgramVertex() {
+        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS);
+        mProgVertex = pvb.create();
+
+        mPVA = new ProgramVertex.MatrixAllocation(mRS);
+        mProgVertex.bindAllocation(mPVA);
+        mPVA.setupOrthoWindow(mWidth, mHeight);
+
+        mScript.set_gProgVertex(mProgVertex);
+    }
+
+    private Allocation loadTextureRGB(int id) {
+        final Allocation allocation = Allocation.createFromBitmapResource(mRS, mRes,
+                id, Element.RGB_565(mRS), true);
+        allocation.uploadToTexture(0);
+        return allocation;
+    }
+
+    private Allocation loadTextureARGB(int id) {
+        Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB);
+        final Allocation allocation = Allocation.createFromBitmap(mRS, b, Element.RGBA_8888(mRS), true);
+        allocation.uploadToTexture(0);
+        return allocation;
+    }
+
+    private void loadImages() {
+        mTexTorus = loadTextureRGB(R.drawable.torusmap);
+        mTexOpaque = loadTextureRGB(R.drawable.data);
+        mTexTransparent = loadTextureARGB(R.drawable.leaf);
+
+        mScript.set_gTexTorus(mTexTorus);
+        mScript.set_gTexOpaque(mTexOpaque);
+        mScript.set_gTexTransparent(mTexTransparent);
+    }
+
+    private void initFonts() {
+        // Sans font by family name
+        mFontSans = Font.createFromFamily(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8);
+        // Create font by file name
+        mFontSerif = Font.create(mRS, mRes, "DroidSerif-Regular.ttf", 8);
+        // Create fonts by family and style
+        mFontSerifBold = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD, 8);
+        mFontSerifItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.ITALIC, 8);
+        mFontSerifBoldItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
+        mFontMono = Font.createFromFamily(mRS, mRes, "mono", Font.Style.NORMAL, 8);
+
+        mScript.set_gFontSans(mFontSans);
+        mScript.set_gFontSerif(mFontSerif);
+        mScript.set_gFontSerifBold(mFontSerifBold);
+        mScript.set_gFontSerifItalic(mFontSerifItalic);
+        mScript.set_gFontSerifBoldItalic(mFontSerifBoldItalic);
+        mScript.set_gFontMono(mFontMono);
+    }
+
+    private void initMesh() {
+        mMbyNMesh = getMbyNMesh(256, 256, 10, 10);
+        mScript.set_gMbyNMesh(mMbyNMesh);
+
+        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus);
+        FileA3D.IndexEntry entry = model.getIndexEntry(0);
+        if(entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
+            Log.e("rs", "could not load model");
+        }
+        else {
+            mTorus = (Mesh)entry.getObject();
+            mScript.set_gTorusMesh(mTorus);
+        }
+    }
+
+    private void initSamplers() {
+        Sampler.Builder bs = new Sampler.Builder(mRS);
+        bs.setMin(Sampler.Value.LINEAR);
+        bs.setMag(Sampler.Value.LINEAR);
+        bs.setWrapS(Sampler.Value.WRAP);
+        bs.setWrapT(Sampler.Value.WRAP);
+        mLinearWrap = bs.create();
+
+        mLinearClamp = Sampler.CLAMP_LINEAR(mRS);
+        mNearestClamp = Sampler.CLAMP_NEAREST(mRS);
+        mMipLinearWrap = Sampler.WRAP_LINEAR_MIP_LINEAR(mRS);
+
+        mScript.set_gLinearClamp(mLinearClamp);
+        mScript.set_gLinearWrap(mLinearWrap);
+        mScript.set_gMipLinearWrap(mMipLinearWrap);
+        mScript.set_gNearestClamp(mNearestClamp);
+    }
+
+    private void initProgramRaster() {
+        mCullBack = ProgramRaster.CULL_BACK(mRS);
+        mCullFront = ProgramRaster.CULL_FRONT(mRS);
+
+        mScript.set_gCullBack(mCullBack);
+        mScript.set_gCullFront(mCullFront);
+    }
+
+    private void initRS() {
+
+        mScript = new ScriptC_Rsrenderstates(mRS, mRes, R.raw.rsrenderstates, true);
+
+        initSamplers();
+        initProgramStore();
+        initProgramFragment();
+        initProgramVertex();
+        initFonts();
+        loadImages();
+        initMesh();
+        initProgramRaster();
+
+        mRS.contextBindRootScript(mScript);
+    }
+}
+
+
+
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerView.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesView.java
similarity index 85%
copy from libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerView.java
copy to libs/rs/java/Samples/src/com/android/samples/RsRenderStatesView.java
index 061cf8e..5548de3 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerView.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.modelviewer;
+package com.android.samples;
 
 import java.io.Writer;
 import java.util.ArrayList;
@@ -39,15 +39,15 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
-public class ModelViewerView extends RSSurfaceView {
+public class RsRenderStatesView extends RSSurfaceView {
 
-    public ModelViewerView(Context context) {
+    public RsRenderStatesView(Context context) {
         super(context);
         //setFocusable(true);
     }
 
     private RenderScriptGL mRS;
-    private ModelViewerRS mRender;
+    private RsRenderStatesRS mRender;
 
 
     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
@@ -55,7 +55,7 @@
         if (mRS == null) {
             mRS = createRenderScript(true);
             mRS.contextSetSurface(w, h, holder.getSurface());
-            mRender = new ModelViewerRS();
+            mRender = new RsRenderStatesRS();
             mRender.init(mRS, getResources(), w, h);
         }
     }
@@ -80,13 +80,13 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev)
     {
-        boolean ret = true;
+        boolean ret = false;
         int act = ev.getAction();
-        if (act == ev.ACTION_UP) {
-            ret = false;
+        if (act == ev.ACTION_DOWN) {
+            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
+            ret = true;
         }
 
-        mRender.touchEvent((int)ev.getX(), (int)ev.getY());
         return ret;
     }
 }
diff --git a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
new file mode 100644
index 0000000..68d9d3c
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
@@ -0,0 +1,345 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.samples)
+
+#include "rs_graphics.rsh"
+
+rs_program_vertex gProgVertex;
+rs_program_fragment gProgFragmentColor;
+rs_program_fragment gProgFragmentTexture;
+
+rs_program_store gProgStoreBlendNoneDepth;
+rs_program_store gProgStoreBlendNone;
+rs_program_store gProgStoreBlendAlpha;
+rs_program_store gProgStoreBlendAdd;
+
+rs_allocation gTexOpaque;
+rs_allocation gTexTorus;
+rs_allocation gTexTransparent;
+
+rs_mesh gMbyNMesh;
+rs_mesh gTorusMesh;
+
+rs_font gFontSans;
+rs_font gFontSerif;
+rs_font gFontSerifBold;
+rs_font gFontSerifItalic;
+rs_font gFontSerifBoldItalic;
+rs_font gFontMono;
+
+int gDisplayMode;
+
+rs_sampler gLinearClamp;
+rs_sampler gLinearWrap;
+rs_sampler gMipLinearWrap;
+rs_sampler gNearestClamp;
+
+rs_program_raster gCullBack;
+rs_program_raster gCullFront;
+
+#pragma rs export_var(gProgVertex, gProgFragmentColor, gProgFragmentTexture)
+#pragma rs export_var(gProgStoreBlendNoneDepth, gProgStoreBlendNone, gProgStoreBlendAlpha, gProgStoreBlendAdd)
+#pragma rs export_var(gTexOpaque, gTexTorus, gTexTransparent)
+#pragma rs export_var(gMbyNMesh, gTorusMesh)
+#pragma rs export_var(gFontSans, gFontSerif, gFontSerifBold, gFontSerifItalic, gFontSerifBoldItalic, gFontMono)
+#pragma rs export_var(gLinearClamp, gLinearWrap, gMipLinearWrap, gNearestClamp)
+#pragma rs export_var(gCullBack, gCullFront)
+
+//What we are showing
+#pragma rs export_var(gDisplayMode)
+
+void init() {
+}
+
+void displayFontSamples() {
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    int yPos = 30;
+    rsgBindFont(gFontSans);
+    rsgDrawText("Sans font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(0.5f, 0.9f, 0.5f, 1.0f);
+    rsgBindFont(gFontSerif);
+    rsgDrawText("Serif font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(0.7f, 0.7f, 0.7f, 1.0f);
+    rsgBindFont(gFontSerifBold);
+    rsgDrawText("Serif Bold font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(0.5f, 0.5f, 0.9f, 1.0f);
+    rsgBindFont(gFontSerifItalic);
+    rsgDrawText("Serif Italic font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontSerifBoldItalic);
+    rsgDrawText("Serif Bold Italic font sample", 30, yPos);
+    yPos += 30;
+    rsgBindFont(gFontMono);
+    rsgDrawText("Monospace font sample", 30, yPos);
+}
+
+void bindProgramVertexOrtho() {
+    // Default vertex sahder
+    rsgBindProgramVertex(gProgVertex);
+    // Setup the projectioni matrix
+    rs_matrix4x4 proj;
+    rsMatrixLoadOrtho(&proj, 0, rsgGetWidth(), rsgGetHeight(), 0, -1,1);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+}
+
+void displayShaderSamples() {
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+
+    float startX = 0, startY = 0;
+    float width = 256, height = 256;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    startX = 200; startY = 0;
+    width = 128; height = 128;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    rsgBindProgramStore(gProgStoreBlendAlpha);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexTransparent);
+    startX = 0; startY = 200;
+    width = 128; height = 128;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    // Fragment program with simple color
+    rsgBindProgramFragment(gProgFragmentColor);
+    rsgProgramFragmentConstantColor(gProgFragmentColor, 0.9, 0.3, 0.3, 1);
+    rsgDrawRect(200, 300, 350, 450, 0);
+    rsgProgramFragmentConstantColor(gProgFragmentColor, 0.3, 0.9, 0.3, 1);
+    rsgDrawRect(50, 400, 400, 600, 0);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Texture shader", 10, 50);
+    rsgDrawText("Alpha-blended texture shader", 10, 280);
+    rsgDrawText("Flat color shader", 100, 450);
+}
+
+void displayBlendingSamples() {
+    int i;
+
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    rsgBindProgramFragment(gProgFragmentColor);
+
+    rsgBindProgramStore(gProgStoreBlendNone);
+    for(i = 0; i < 3; i ++) {
+        float iPlusOne = (float)(i + 1);
+        rsgProgramFragmentConstantColor(gProgFragmentColor,
+                                        0.1f*iPlusOne, 0.2f*iPlusOne, 0.3f*iPlusOne, 1);
+        float yPos = 150 * (float)i;
+        rsgDrawRect(0, yPos, 200, yPos + 200, 0);
+    }
+
+    rsgBindProgramStore(gProgStoreBlendAlpha);
+    for(i = 0; i < 3; i ++) {
+        float iPlusOne = (float)(i + 1);
+        rsgProgramFragmentConstantColor(gProgFragmentColor,
+                                        0.2f*iPlusOne, 0.3f*iPlusOne, 0.1f*iPlusOne, 0.5);
+        float yPos = 150 * (float)i;
+        rsgDrawRect(150, yPos, 350, yPos + 200, 0);
+    }
+
+    rsgBindProgramStore(gProgStoreBlendAdd);
+    for(i = 0; i < 3; i ++) {
+        float iPlusOne = (float)(i + 1);
+        rsgProgramFragmentConstantColor(gProgFragmentColor,
+                                        0.3f*iPlusOne, 0.1f*iPlusOne, 0.2f*iPlusOne, 0.5);
+        float yPos = 150 * (float)i;
+        rsgDrawRect(300, yPos, 500, yPos + 200, 0);
+    }
+
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("No Blending", 10, 50);
+    rsgDrawText("Alpha Blending", 160, 150);
+    rsgDrawText("Additive Blending", 320, 250);
+
+}
+
+void displayMeshSamples() {
+
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadTranslate(&matrix, 128, 128, 0);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+
+    rsgDrawMesh(gMbyNMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("User gen 10 by 10 grid mesh", 10, 250);
+}
+
+void displayTextureSamplers() {
+
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+
+    // Linear clamp
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    float startX = 0, startY = 0;
+    float width = 300, height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.1,
+                         startX + width, startY + height, 0, 1.1, 1.1,
+                         startX + width, startY, 0, 1.1, 0);
+
+    // Linear Wrap
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearWrap);
+    startX = 0; startY = 300;
+    width = 300; height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.1,
+                         startX + width, startY + height, 0, 1.1, 1.1,
+                         startX + width, startY, 0, 1.1, 0);
+
+    // Nearest
+    rsgBindSampler(gProgFragmentTexture, 0, gNearestClamp);
+    startX = 300; startY = 0;
+    width = 300; height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.1,
+                         startX + width, startY + height, 0, 1.1, 1.1,
+                         startX + width, startY, 0, 1.1, 0);
+
+    rsgBindSampler(gProgFragmentTexture, 0, gMipLinearWrap);
+    startX = 300; startY = 300;
+    width = 300; height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.5,
+                         startX + width, startY + height, 0, 1.5, 1.5,
+                         startX + width, startY, 0, 1.5, 0);
+
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Filtering: linear clamp", 10, 290);
+    rsgDrawText("Filtering: linear wrap", 10, 590);
+    rsgDrawText("Filtering: nearest clamp", 310, 290);
+    rsgDrawText("Filtering: miplinear wrap", 310, 590);
+
+}
+
+float gTorusRotation = 0;
+
+void displayCullingSamplers() {
+    rsgBindProgramVertex(gProgVertex);
+    // Setup the projectioni matrix with 60 degree field of view
+    rs_matrix4x4 proj;
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexTorus);
+
+    // Aplly a rotation to our mesh
+    gTorusRotation += 50.0f * rsGetDt();
+    if(gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    rs_matrix4x4 matrix;
+    // Position our model on the screen
+    rsMatrixLoadTranslate(&matrix, -2.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+    // Use front face culling
+    rsgBindProgramRaster(gCullFront);
+    rsgDrawMesh(gTorusMesh);
+
+    rsMatrixLoadTranslate(&matrix, 2.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+    // Use back face culling
+    rsgBindProgramRaster(gCullBack);
+    rsgDrawMesh(gTorusMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Displaying mesh front/back face culling", 10, rsgGetHeight() - 10);
+}
+
+int root(int launchID) {
+
+    rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f);
+    rsgClearDepth(1.0f);
+
+    switch(gDisplayMode) {
+    case 0:
+        displayFontSamples();
+        break;
+    case 1:
+        displayShaderSamples();
+        break;
+    case 2:
+        displayBlendingSamples();
+        break;
+    case 3:
+        displayMeshSamples();
+        break;
+    case 4:
+        displayTextureSamplers();
+        break;
+    case 5:
+        displayCullingSamplers();
+        break;
+    }
+
+    return 10;
+}
diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp
index fbaa75f..f51b23e 100644
--- a/libs/rs/rsComponent.cpp
+++ b/libs/rs/rsComponent.cpp
@@ -95,6 +95,26 @@
         mNormalized = true;
         rsAssert(mKind == RS_KIND_PIXEL_RGBA);
         return;
+
+    case RS_TYPE_MATRIX_4X4:
+        mTypeBits = 16 * 32;
+        rsAssert(mVectorSize == 1);
+        rsAssert(mNormalized == false);
+        rsAssert(mKind == RS_KIND_USER);
+        break;
+    case RS_TYPE_MATRIX_3X3:
+        mTypeBits = 9 * 32;
+        rsAssert(mVectorSize == 1);
+        rsAssert(mNormalized == false);
+        rsAssert(mKind == RS_KIND_USER);
+        break;
+    case RS_TYPE_MATRIX_2X2:
+        mTypeBits = 4 * 32;
+        rsAssert(mVectorSize == 1);
+        rsAssert(mNormalized == false);
+        rsAssert(mKind == RS_KIND_USER);
+        break;
+
     case RS_TYPE_ELEMENT:
     case RS_TYPE_TYPE:
     case RS_TYPE_ALLOCATION:
@@ -218,10 +238,19 @@
         case 4: return String8("vec4");
         }
     }
+    if ((mType == RS_TYPE_MATRIX_4X4) && (mVectorSize == 1)) {
+        return String8("mat4");
+    }
+    if ((mType == RS_TYPE_MATRIX_3X3) && (mVectorSize == 1)) {
+        return String8("mat3");
+    }
+    if ((mType == RS_TYPE_MATRIX_2X2) && (mVectorSize == 1)) {
+        return String8("mat2");
+    }
     return String8();
 }
 
-static const char * gTypeStrings[] = {
+static const char * gTypeBasicStrings[] = {
     "NONE",
     "F16",
     "F32",
@@ -238,6 +267,12 @@
     "UP_565",
     "UP_5551",
     "UP_4444",
+    "MATRIX_4X4",
+    "MATRIX_3X3",
+    "MATRIX_2X2",
+};
+
+static const char * gTypeObjStrings[] = {
     "ELEMENT",
     "TYPE",
     "ALLOCATION",
@@ -267,8 +302,13 @@
 
 void Component::dumpLOGV(const char *prefix) const
 {
-    LOGV("%s   Component: %s, %s, vectorSize=%i, bits=%i",
-         prefix, gTypeStrings[mType], gKindStrings[mKind], mVectorSize, mBits);
+    if (mType >= RS_TYPE_ELEMENT) {
+        LOGV("%s   Component: %s, %s, vectorSize=%i, bits=%i",
+             prefix, gTypeObjStrings[mType - RS_TYPE_ELEMENT], gKindStrings[mKind], mVectorSize, mBits);
+    } else {
+        LOGV("%s   Component: %s, %s, vectorSize=%i, bits=%i",
+             prefix, gTypeBasicStrings[mType], gKindStrings[mKind], mVectorSize, mBits);
+    }
 }
 
 void Component::serialize(OStream *stream) const
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 08dd57b..f67a9d5 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -713,10 +713,10 @@
 uint32_t Context::getMessageToClient(void *data, size_t *receiveLen, size_t bufferLen, bool wait)
 {
     //LOGE("getMessageToClient %i %i", bufferLen, wait);
+    *receiveLen = 0;
     if (!wait) {
         if (mIO.mToClient.isEmpty()) {
             // No message to get and not going to wait for one.
-            receiveLen = 0;
             return 0;
         }
     }
diff --git a/libs/rs/scriptc/rs_core.rsh b/libs/rs/scriptc/rs_core.rsh
index 0bfb3b9..99fc166 100644
--- a/libs/rs/scriptc/rs_core.rsh
+++ b/libs/rs/scriptc/rs_core.rsh
@@ -429,6 +429,15 @@
     m->m[15]= 0.f;
 }
 
+static void __attribute__((overloadable))
+rsMatrixLoadPerspective(rs_matrix4x4* m, float fovy, float aspect, float near, float far) {
+    float top = near * tan((float) (fovy * M_PI / 360.0f));
+    float bottom = -top;
+    float left = bottom * aspect;
+    float right = top * aspect;
+    rsMatrixLoadFrustum(m, left, right, bottom, top, near, far);
+}
+
 static float4 __attribute__((overloadable))
 rsMatrixMultiply(rs_matrix4x4 *m, float4 in) {
     float4 ret;
diff --git a/libs/storage/Android.mk b/libs/storage/Android.mk
new file mode 100644
index 0000000..1e52fa4
--- /dev/null
+++ b/libs/storage/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	IMountServiceListener.cpp \
+	IMountShutdownObserver.cpp \
+	IObbActionListener.cpp \
+	IMountService.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+	libutils \
+	libbinder
+
+LOCAL_MODULE:= libstorage
+
+ifeq ($(TARGET_SIMULATOR),true)
+    LOCAL_LDLIBS += -lpthread
+endif
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
new file mode 100644
index 0000000..902bb27
--- /dev/null
+++ b/libs/storage/IMountService.cpp
@@ -0,0 +1,508 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "IMountService"
+
+#include <storage/IMountService.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+    TRANSACTION_registerListener = IBinder::FIRST_CALL_TRANSACTION,
+    TRANSACTION_unregisterListener,
+    TRANSACTION_isUsbMassStorageConnected,
+    TRANSACTION_setUsbMassStorageEnabled,
+    TRANSACTION_isUsbMassStorageEnabled,
+    TRANSACTION_mountVolume,
+    TRANSACTION_unmountVolume,
+    TRANSACTION_formatVolume,
+    TRANSACTION_getStorageUsers,
+    TRANSACTION_getVolumeState,
+    TRANSACTION_createSecureContainer,
+    TRANSACTION_finalizeSecureContainer,
+    TRANSACTION_destroySecureContainer,
+    TRANSACTION_mountSecureContainer,
+    TRANSACTION_unmountSecureContainer,
+    TRANSACTION_isSecureContainerMounted,
+    TRANSACTION_renameSecureContainer,
+    TRANSACTION_getSecureContainerPath,
+    TRANSACTION_getSecureContainerList,
+    TRANSACTION_shutdown,
+    TRANSACTION_finishMediaUpdate,
+    TRANSACTION_mountObb,
+    TRANSACTION_unmountObb,
+    TRANSACTION_isObbMounted,
+    TRANSACTION_getMountedObbPath,
+};
+
+class BpMountService: public BpInterface<IMountService>
+{
+public:
+    BpMountService(const sp<IBinder>& impl)
+        : BpInterface<IMountService>(impl)
+    {
+    }
+
+    virtual void registerListener(const sp<IMountServiceListener>& listener)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeStrongBinder(listener->asBinder());
+        if (remote()->transact(TRANSACTION_registerListener, data, &reply) != NO_ERROR) {
+            LOGD("registerListener could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("registerListener caught exception %d\n", err);
+            return;
+        }
+    }
+
+    virtual void unregisterListener(const sp<IMountServiceListener>& listener)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeStrongBinder(listener->asBinder());
+        if (remote()->transact(TRANSACTION_unregisterListener, data, &reply) != NO_ERROR) {
+            LOGD("unregisterListener could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("unregisterListener caught exception %d\n", err);
+            return;
+        }
+    }
+
+    virtual bool isUsbMassStorageConnected()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        if (remote()->transact(TRANSACTION_isUsbMassStorageConnected, data, &reply) != NO_ERROR) {
+            LOGD("isUsbMassStorageConnected could not contact remote\n");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("isUsbMassStorageConnected caught exception %d\n", err);
+            return false;
+        }
+        return reply.readInt32() != 0;
+    }
+
+    virtual void setUsbMassStorageEnabled(const bool enable)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeInt32(enable != 0);
+        if (remote()->transact(TRANSACTION_setUsbMassStorageEnabled, data, &reply) != NO_ERROR) {
+            LOGD("setUsbMassStorageEnabled could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("setUsbMassStorageEnabled caught exception %d\n", err);
+            return;
+        }
+    }
+
+    virtual bool isUsbMassStorageEnabled()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        if (remote()->transact(TRANSACTION_isUsbMassStorageEnabled, data, &reply) != NO_ERROR) {
+            LOGD("isUsbMassStorageEnabled could not contact remote\n");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("isUsbMassStorageEnabled caught exception %d\n", err);
+            return false;
+        }
+        return reply.readInt32() != 0;
+    }
+
+    int32_t mountVolume(const String16& mountPoint)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(mountPoint);
+        if (remote()->transact(TRANSACTION_mountVolume, data, &reply) != NO_ERROR) {
+            LOGD("mountVolume could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("mountVolume caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t unmountVolume(const String16& mountPoint, const bool force)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(mountPoint);
+        data.writeInt32(force ? 1 : 0);
+        if (remote()->transact(TRANSACTION_unmountVolume, data, &reply) != NO_ERROR) {
+            LOGD("unmountVolume could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("unmountVolume caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t formatVolume(const String16& mountPoint)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(mountPoint);
+        if (remote()->transact(TRANSACTION_formatVolume, data, &reply) != NO_ERROR) {
+            LOGD("formatVolume could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("formatVolume caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t getStorageUsers(const String16& mountPoint, int32_t** users)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(mountPoint);
+        if (remote()->transact(TRANSACTION_getStorageUsers, data, &reply) != NO_ERROR) {
+            LOGD("getStorageUsers could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("getStorageUsers caught exception %d\n", err);
+            return err;
+        }
+        const int32_t numUsers = reply.readInt32();
+        *users = (int32_t*)malloc(sizeof(int32_t)*numUsers);
+        for (int i = 0; i < numUsers; i++) {
+            **users++ = reply.readInt32();
+        }
+        return numUsers;
+    }
+
+    int32_t getVolumeState(const String16& mountPoint)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(mountPoint);
+        if (remote()->transact(TRANSACTION_getVolumeState, data, &reply) != NO_ERROR) {
+            LOGD("getVolumeState could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("getVolumeState caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t createSecureContainer(const String16& id, const int32_t sizeMb, const String16& fstype,
+            const String16& key, const int32_t ownerUid)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        data.writeInt32(sizeMb);
+        data.writeString16(fstype);
+        data.writeString16(key);
+        data.writeInt32(ownerUid);
+        if (remote()->transact(TRANSACTION_createSecureContainer, data, &reply) != NO_ERROR) {
+            LOGD("createSecureContainer could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("createSecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t finalizeSecureContainer(const String16& id)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        if (remote()->transact(TRANSACTION_finalizeSecureContainer, data, &reply) != NO_ERROR) {
+            LOGD("finalizeSecureContainer couldn't call remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("finalizeSecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t destroySecureContainer(const String16& id)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        if (remote()->transact(TRANSACTION_destroySecureContainer, data, &reply) != NO_ERROR) {
+            LOGD("destroySecureContainer couldn't call remote");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("destroySecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t mountSecureContainer(const String16& id, const String16& key, const int32_t ownerUid)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        data.writeString16(key);
+        data.writeInt32(ownerUid);
+        if (remote()->transact(TRANSACTION_mountSecureContainer, data, &reply) != NO_ERROR) {
+            LOGD("mountSecureContainer couldn't call remote");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode(); // What to do...
+        if (err < 0) {
+            LOGD("mountSecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t unmountSecureContainer(const String16& id, const bool force)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        data.writeInt32(force ? 1 : 0);
+        if (remote()->transact(TRANSACTION_getSecureContainerPath, data, &reply) != NO_ERROR) {
+            LOGD("unmountSecureContainer couldn't call remote");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode(); // What to do...
+        if (err < 0) {
+            LOGD("unmountSecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    bool isSecureContainerMounted(const String16& id)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        if (remote()->transact(TRANSACTION_isSecureContainerMounted, data, &reply) != NO_ERROR) {
+            LOGD("isSecureContainerMounted couldn't call remote");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode(); // What to do...
+        if (err < 0) {
+            LOGD("isSecureContainerMounted caught exception %d\n", err);
+            return false;
+        }
+        return reply.readInt32() != 0;
+    }
+
+    int32_t renameSecureContainer(const String16& oldId, const String16& newId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(oldId);
+        data.writeString16(newId);
+        if (remote()->transact(TRANSACTION_renameSecureContainer, data, &reply) != NO_ERROR) {
+            LOGD("renameSecureContainer couldn't call remote");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode(); // What to do...
+        if (err < 0) {
+            LOGD("renameSecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    bool getSecureContainerPath(const String16& id, String16& path)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        if (remote()->transact(TRANSACTION_getSecureContainerPath, data, &reply) != NO_ERROR) {
+            LOGD("getSecureContainerPath couldn't call remote");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode(); // What to do...
+        if (err < 0) {
+            LOGD("getSecureContainerPath caught exception %d\n", err);
+            return false;
+        }
+        path = reply.readString16();
+        return true;
+    }
+
+    int32_t getSecureContainerList(const String16& id, String16*& containers)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        if (remote()->transact(TRANSACTION_getSecureContainerList, data, &reply) != NO_ERROR) {
+            LOGD("getSecureContainerList couldn't call remote");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("getSecureContainerList caught exception %d\n", err);
+            return err;
+        }
+        const int32_t numStrings = reply.readInt32();
+        containers = new String16[numStrings];
+        for (int i = 0; i < numStrings; i++) {
+            containers[i] = reply.readString16();
+        }
+        return numStrings;
+    }
+
+    void shutdown(const sp<IMountShutdownObserver>& observer)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeStrongBinder(observer->asBinder());
+        if (remote()->transact(TRANSACTION_shutdown, data, &reply) != NO_ERROR) {
+            LOGD("shutdown could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("shutdown caught exception %d\n", err);
+            return;
+        }
+        reply.readExceptionCode();
+    }
+
+    void finishMediaUpdate()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        if (remote()->transact(TRANSACTION_finishMediaUpdate, data, &reply) != NO_ERROR) {
+            LOGD("finishMediaUpdate could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("finishMediaUpdate caught exception %d\n", err);
+            return;
+        }
+        reply.readExceptionCode();
+    }
+
+    void mountObb(const String16& filename, const String16& key, const sp<
+            IObbActionListener>& token)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(filename);
+        data.writeString16(key);
+        data.writeStrongBinder(token->asBinder());
+        if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) {
+            LOGD("mountObb could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("mountObb caught exception %d\n", err);
+            return;
+        }
+    }
+
+    void unmountObb(const String16& filename, const bool force)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(filename);
+        data.writeInt32(force ? 1 : 0);
+        if (remote()->transact(TRANSACTION_unmountObb, data, &reply) != NO_ERROR) {
+            LOGD("unmountObb could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("unmountObb caught exception %d\n", err);
+            return;
+        }
+    }
+
+    bool isObbMounted(const String16& filename)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(filename);
+        if (remote()->transact(TRANSACTION_isObbMounted, data, &reply) != NO_ERROR) {
+            LOGD("isObbMounted could not contact remote\n");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("isObbMounted caught exception %d\n", err);
+            return false;
+        }
+        return reply.readInt32() != 0;
+    }
+
+    bool getMountedObbPath(const String16& filename, String16& path)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(filename);
+        if (remote()->transact(TRANSACTION_getMountedObbPath, data, &reply) != NO_ERROR) {
+            LOGD("getMountedObbPath could not contact remote\n");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("getMountedObbPath caught exception %d\n", err);
+            return false;
+        }
+        path = reply.readString16();
+        return true;
+    }
+};
+
+IMPLEMENT_META_INTERFACE(MountService, "IMountService");
+
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/IMountServiceListener.cpp b/libs/storage/IMountServiceListener.cpp
new file mode 100644
index 0000000..c98a424
--- /dev/null
+++ b/libs/storage/IMountServiceListener.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <storage/IMountServiceListener.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+    TRANSACTION_onUsbMassStorageConnectionChanged = IBinder::FIRST_CALL_TRANSACTION,
+    TRANSACTION_onStorageStateChanged,
+};
+
+status_t BnMountServiceListener::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case TRANSACTION_onUsbMassStorageConnectionChanged: {
+            CHECK_INTERFACE(IMountServiceListener, data, reply);
+            bool connected = (data.readInt32() != 0);
+            onUsbMassStorageConnectionChanged(connected);
+            reply->writeNoException();
+            return NO_ERROR;
+        } break;
+        case TRANSACTION_onStorageStateChanged: {
+            CHECK_INTERFACE(IMountServiceListener, data, reply);
+            String16 path = data.readString16();
+            String16 oldState = data.readString16();
+            String16 newState = data.readString16();
+            onStorageStateChanged(path, oldState, newState);
+            reply->writeNoException();
+            return NO_ERROR;
+        }
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/IMountShutdownObserver.cpp b/libs/storage/IMountShutdownObserver.cpp
new file mode 100644
index 0000000..1a6fdee
--- /dev/null
+++ b/libs/storage/IMountShutdownObserver.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <storage/IMountShutdownObserver.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+    TRANSACTION_onShutDownComplete = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+status_t BnMountShutdownObserver::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case TRANSACTION_onShutDownComplete: {
+            CHECK_INTERFACE(IMountShutdownObserver, data, reply);
+            int32_t statusCode = data.readInt32();
+            onShutDownComplete(statusCode);
+            reply->writeNoException();
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/IObbActionListener.cpp b/libs/storage/IObbActionListener.cpp
new file mode 100644
index 0000000..5bfece7
--- /dev/null
+++ b/libs/storage/IObbActionListener.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <storage/IObbActionListener.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+    TRANSACTION_onObbResult = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+// This is a stub that real consumers should override.
+class BpObbActionListener: public BpInterface<IObbActionListener> {
+public:
+    BpObbActionListener(const sp<IBinder>& impl)
+        : BpInterface<IObbActionListener>(impl)
+    { }
+
+    virtual void onObbResult(const String16& filename, const String16& status) { }
+};
+
+IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener");
+
+// ----------------------------------------------------------------------
+
+status_t BnObbActionListener::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case TRANSACTION_onObbResult: {
+            CHECK_INTERFACE(IObbActionListener, data, reply);
+            String16 filename = data.readString16();
+            String16 state = data.readString16();
+            onObbResult(filename, state);
+            reply->writeNoException();
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/MODULE_LICENSE_APACHE2 b/libs/storage/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/storage/MODULE_LICENSE_APACHE2
diff --git a/libs/storage/NOTICE b/libs/storage/NOTICE
new file mode 100644
index 0000000..5d14293
--- /dev/null
+++ b/libs/storage/NOTICE
@@ -0,0 +1,190 @@
+
+   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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 156a7db..4c01cd2 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -75,6 +75,14 @@
     return NO_ERROR;
 }
 
+status_t SharedBufferStack::setTransform(int buffer, uint8_t transform)
+{
+    if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+        return BAD_INDEX;
+    buffers[buffer].transform = transform;
+    return NO_ERROR;
+}
+
 status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty)
 {
     if (uint32_t(buffer) >= NUM_BUFFER_MAX)
@@ -137,6 +145,26 @@
     return res;
 }
 
+Rect SharedBufferStack::getCrop(int buffer) const
+{
+    Rect res(-1, -1);
+    if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+        return res;
+    res.left = buffers[buffer].crop.l;
+    res.top = buffers[buffer].crop.t;
+    res.right = buffers[buffer].crop.r;
+    res.bottom = buffers[buffer].crop.b;
+    return res;
+}
+
+uint32_t SharedBufferStack::getTransform(int buffer) const
+{
+    if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+        return 0;
+    return buffers[buffer].transform;
+}
+
+
 // ----------------------------------------------------------------------------
 
 SharedBufferBase::SharedBufferBase(SharedClient* sharedClient,
@@ -344,11 +372,6 @@
 {
     SharedBufferStack& stack( *mSharedStack );
 
-    if (stack.head == tail && stack.available == mNumBuffers) {
-        LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d",
-                tail, stack.head, stack.available, stack.queued);
-    }
-
     RWLock::AutoRLock _rd(mLock);
 
     const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD);
@@ -433,6 +456,12 @@
     return stack.setCrop(buf, crop);
 }
 
+status_t SharedBufferClient::setTransform(int buf, uint32_t transform)
+{
+    SharedBufferStack& stack( *mSharedStack );
+    return stack.setTransform(buf, uint8_t(transform));
+}
+
 status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg)
 {
     SharedBufferStack& stack( *mSharedStack );
@@ -549,6 +578,18 @@
     return stack.getDirtyRegion(buf);
 }
 
+Rect SharedBufferServer::getCrop(int buf) const
+{
+    SharedBufferStack& stack( *mSharedStack );
+    return stack.getCrop(buf);
+}
+
+uint32_t SharedBufferServer::getTransform(int buf) const
+{
+    SharedBufferStack& stack( *mSharedStack );
+    return stack.getTransform(buf);
+}
+
 /*
  * NOTE: this is not thread-safe on the server-side, meaning
  * 'head' cannot move during this operation. The client-side
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 5ab72cd..2bc5fad 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -363,6 +363,13 @@
         height   = surface->mHeight;
         format   = surface->mFormat;
         flags    = surface->mFlags;
+    } else if (surface != 0 && surface->mSurface != 0) {
+        LOGW("Parceling invalid surface with non-NULL ISurface as NULL: "
+             "mSurface = %p, mIdentity = %d, mWidth = %d, mHeight = %d, "
+             "mFormat = %d, mFlags = 0x%08x, mInitCheck = %d",
+             surface->mSurface.get(), surface->mIdentity, surface->mWidth,
+             surface->mHeight, surface->mFormat, surface->mFlags,
+             surface->mInitCheck);
     }
     parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
     parcel->writeInt32(identity);
@@ -422,8 +429,10 @@
     const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
     const_cast<uint32_t&>(ANativeWindow::flags) = 0;
 
+    mNextBufferTransform = 0;
     mConnected = 0;
     mSwapRectangle.makeInvalid();
+    mNextBufferCrop = Rect(0,0);
     // two buffers by default
     mBuffers.setCapacity(2);
     mBuffers.insertAt(0, 2);
@@ -434,6 +443,9 @@
             mSharedBufferClient = new SharedBufferClient(
                     mClient.getSharedClient(), token, 2, mIdentity);
             mInitCheck = mClient.getSharedClient()->validate(token);
+        } else {
+            LOGW("Not initializing the shared buffer client because token = %d",
+                    token);
         }
     }
 }
@@ -631,6 +643,7 @@
     }
     
     int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+    mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform);
     mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop);
     mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion);
     err = mSharedBufferClient->queue(bufIdx);
@@ -685,6 +698,9 @@
     case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
         res = dispatch_set_buffers_geometry( args );
         break;
+    case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+        res = dispatch_set_buffers_transform( args );
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -719,6 +735,11 @@
     return setBuffersGeometry(w, h, f);
 }
 
+int Surface::dispatch_set_buffers_transform(va_list args) {
+    int transform = va_arg(args, int);
+    return setBuffersTransform(transform);
+}
+
 void Surface::setUsage(uint32_t reqUsage)
 {
     Mutex::Autolock _l(mSurfaceLock);
@@ -765,6 +786,10 @@
 
 int Surface::crop(Rect const* rect)
 {
+    // empty/invalid rects are not allowed
+    if (rect->isEmpty())
+        return BAD_VALUE;
+
     Mutex::Autolock _l(mSurfaceLock);
     // TODO: validate rect size
     mNextBufferCrop = *rect;
@@ -804,6 +829,13 @@
     return NO_ERROR;
 }
 
+int Surface::setBuffersTransform(int transform)
+{
+    Mutex::Autolock _l(mSurfaceLock);
+    mNextBufferTransform = transform;
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 int Surface::getConnectedApi() const
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 13030b5..e35050c 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -28,6 +28,9 @@
 // Log debug messages about input event injection.
 #define DEBUG_INJECTION 0
 
+// Log debug messages about input event throttling.
+#define DEBUG_THROTTLING 0
+
 #include <cutils/log.h>
 #include <ui/InputDispatcher.h>
 
@@ -66,6 +69,15 @@
 
     mKeyRepeatState.lastKeyEntry = NULL;
 
+    int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond();
+    mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond;
+    mThrottleState.lastDeviceId = -1;
+
+#if DEBUG_THROTTLING
+    mThrottleState.originalSampleCount = 0;
+    LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
+#endif
+
     mCurrentInputTargetsValid = false;
 }
 
@@ -144,12 +156,61 @@
                 }
             } else {
                 // Inbound queue has at least one entry.
-                // Start processing it but leave it on the queue until later so that the
+                EventEntry* entry = mInboundQueue.head.next;
+
+                // Consider throttling the entry if it is a move event and there are no
+                // other events behind it in the queue.  Due to movement batching, additional
+                // samples may be appended to this event by the time the throttling timeout
+                // expires.
+                // TODO Make this smarter and consider throttling per device independently.
+                if (entry->type == EventEntry::TYPE_MOTION) {
+                    MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+                    int32_t deviceId = motionEntry->deviceId;
+                    uint32_t source = motionEntry->source;
+                    if (motionEntry->next == & mInboundQueue.tail
+                            && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
+                            && deviceId == mThrottleState.lastDeviceId
+                            && source == mThrottleState.lastSource) {
+                        nsecs_t nextTime = mThrottleState.lastEventTime
+                                + mThrottleState.minTimeBetweenEvents;
+                        if (currentTime < nextTime) {
+                            // Throttle it!
+#if DEBUG_THROTTLING
+                            LOGD("Throttling - Delaying motion event for "
+                                    "device 0x%x, source 0x%08x by up to %0.3fms.",
+                                    deviceId, source, (nextTime - currentTime) * 0.000001);
+#endif
+                            if (nextTime < nextWakeupTime) {
+                                nextWakeupTime = nextTime;
+                            }
+                            if (mThrottleState.originalSampleCount == 0) {
+                                mThrottleState.originalSampleCount =
+                                        motionEntry->countSamples();
+                            }
+                            goto Throttle;
+                        }
+                    }
+
+#if DEBUG_THROTTLING
+                    if (mThrottleState.originalSampleCount != 0) {
+                        uint32_t count = motionEntry->countSamples();
+                        LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
+                                count - mThrottleState.originalSampleCount,
+                                mThrottleState.originalSampleCount, count);
+                        mThrottleState.originalSampleCount = 0;
+                    }
+#endif
+
+                    mThrottleState.lastEventTime = entry->eventTime < currentTime
+                            ? entry->eventTime : currentTime;
+                    mThrottleState.lastDeviceId = deviceId;
+                    mThrottleState.lastSource = source;
+                }
+
+                // Start processing the entry but leave it on the queue until later so that the
                 // input reader can keep appending samples onto a motion event between the
                 // time we started processing it and the time we finally enqueue dispatch
                 // entries for it.
-                EventEntry* entry = mInboundQueue.head.next;
-
                 switch (entry->type) {
                 case EventEntry::TYPE_CONFIGURATION_CHANGED: {
                     ConfigurationChangedEntry* typedEntry =
@@ -179,6 +240,8 @@
                 mInboundQueue.dequeue(entry);
                 mAllocator.releaseEventEntry(entry);
                 skipPoll = true;
+
+            Throttle: ;
             }
         }
 
@@ -192,8 +255,8 @@
         return;
     }
 
-    // Wait for callback or timeout or wake.
-    nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime);
+    // Wait for callback or timeout or wake.  (make sure we round up, not down)
+    nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL;
     int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
     mPollLoop->pollOnce(timeoutMillis);
 }
@@ -1708,6 +1771,16 @@
     motionEntry->lastSample = sample;
 }
 
+// --- InputDispatcher::MotionEntry ---
+
+uint32_t InputDispatcher::MotionEntry::countSamples() const {
+    uint32_t count = 1;
+    for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) {
+        count += 1;
+    }
+    return count;
+}
+
 // --- InputDispatcher::Connection ---
 
 InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index edf1aed..ee186c8 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -63,7 +63,6 @@
         info->bitsPerPixel = 16;
         goto done;
     case HAL_PIXEL_FORMAT_YCrCb_420_SP:
-    case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
     case HAL_PIXEL_FORMAT_YV12:
         info->bitsPerPixel = 12;
      done:
diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp
index adedf0c..e170ab8 100644
--- a/libs/utils/ObbFile.cpp
+++ b/libs/utils/ObbFile.cpp
@@ -29,12 +29,13 @@
 
 #define kFooterTagSize 8  /* last two 32-bit integers */
 
-#define kFooterMinSize 21 /* 32-bit signature version
-                           * 32-bit package version
-                           * 32-bit package name size
-                           * 1-character package name
-                           * 32-bit footer size
-                           * 32-bit footer marker
+#define kFooterMinSize 25 /* 32-bit signature version (4 bytes)
+                           * 32-bit package version (4 bytes)
+                           * 32-bit flags (4 bytes)
+                           * 32-bit package name size (4-bytes)
+                           * >=1-character package name (1 byte)
+                           * 32-bit footer size (4 bytes)
+                           * 32-bit footer marker (4 bytes)
                            */
 
 #define kMaxBufSize    32768 /* Maximum file read buffer */
@@ -45,8 +46,9 @@
 
 /* offsets in version 1 of the header */
 #define kPackageVersionOffset 4
-#define kPackageNameLenOffset 8
-#define kPackageNameOffset    12
+#define kFlagsOffset          8
+#define kPackageNameLenOffset 12
+#define kPackageNameOffset    16
 
 /*
  * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
@@ -78,7 +80,10 @@
 namespace android {
 
 ObbFile::ObbFile() :
-        mVersion(-1) {
+        mPackageName(""),
+        mVersion(-1),
+        mFlags(0)
+{
 }
 
 ObbFile::~ObbFile() {
@@ -199,6 +204,7 @@
     }
 
     mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);
+    mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset);
 
     uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
     if (packageNameLen <= 0
@@ -268,6 +274,12 @@
         return false;
     }
 
+    put4LE(intBuf, mFlags);
+    if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+        LOGW("couldn't write package version");
+        return false;
+    }
+
     size_t packageNameLen = mPackageName.size();
     put4LE(intBuf, packageNameLen);
     if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
@@ -280,7 +292,7 @@
         return false;
     }
 
-    put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen);
+    put4LE(intBuf, kPackageNameOffset + packageNameLen);
     if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
         LOGW("couldn't write footer size: %s", strerror(errno));
         return false;
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index a4c3500..604f558 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -636,7 +636,7 @@
         memcpy(buffer, ptr, uncompLen);
     } else {
         if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     if (compLen > kSequentialMin)
@@ -644,6 +644,8 @@
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
@@ -667,7 +669,7 @@
 
     getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
 
-    const FileMap* file = createEntryFileMap(entry);
+    FileMap* file = createEntryFileMap(entry);
     if (file == NULL) {
         goto bail;
     }
@@ -678,21 +680,23 @@
         ssize_t actual = write(fd, ptr, uncompLen);
         if (actual < 0) {
             LOGE("Write failed: %s\n", strerror(errno));
-            goto bail;
+            goto unmap;
         } else if ((size_t) actual != uncompLen) {
             LOGE("Partial write during uncompress (%zd of %zd)\n",
                 (size_t)actual, (size_t)uncompLen);
-            goto bail;
+            goto unmap;
         } else {
             LOGI("+++ successful write\n");
         }
     } else {
         if (!inflateBuffer(fd, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
diff --git a/media/java/android/media/BassBoost.java b/media/java/android/media/BassBoost.java
index 73c1751..476b056 100644
--- a/media/java/android/media/BassBoost.java
+++ b/media/java/android/media/BassBoost.java
@@ -99,7 +99,7 @@
            UnsupportedOperationException, RuntimeException {
         super(EFFECT_TYPE_BASS_BOOST, EFFECT_TYPE_NULL, priority, audioSession);
 
-        short[] value = new short[1];
+        int[] value = new int[1];
         checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
         mStrengthSupported = (value[0] != 0);
     }
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 7f91c22..7d8ef58 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -556,7 +556,8 @@
                 mTitle = value;
             } else if (name.equalsIgnoreCase("artist") || name.startsWith("artist;")) {
                 mArtist = value.trim();
-            } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")) {
+            } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")
+                    || name.equalsIgnoreCase("band") || name.startsWith("band;")) {
                 mAlbumArtist = value.trim();
             } else if (name.equalsIgnoreCase("album") || name.startsWith("album;")) {
                 mAlbum = value.trim();
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 88cce46..7e0d390 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -66,14 +66,6 @@
 
     private final MediaScanner mMediaScanner;
 
-    // MTP property codes
-    private static final int MTP_PROPERTY_STORAGE_ID = 0xDC01;
-    private static final int MTP_PROPERTY_OBJECT_FORMAT = 0xDC02;
-    private static final int MTP_PROPERTY_OBJECT_SIZE = 0xDC04;
-    private static final int MTP_PROPERTY_OBJECT_FILE_NAME = 0xDC07;
-    private static final int MTP_PROPERTY_DATE_MODIFIED = 0xDC09;
-    private static final int MTP_PROPERTY_PARENT_OBJECT = 0xDC0B;
-
     // MTP response codes
     private static final int MTP_RESPONSE_OK = 0x2001;
     private static final int MTP_RESPONSE_GENERAL_ERROR = 0x2002;
@@ -271,25 +263,36 @@
         boolean isString = false;
 
         switch (property) {
-            case MTP_PROPERTY_STORAGE_ID:
+            case Mtp.Object.PROPERTY_STORAGE_ID:
                 outIntValue[0] = mStorageID;
                 return MTP_RESPONSE_OK;
-            case MTP_PROPERTY_OBJECT_FORMAT:
+            case Mtp.Object.PROPERTY_OBJECT_FORMAT:
                 column = MtpObjects.ObjectColumns.FORMAT;
                 break;
-            case MTP_PROPERTY_OBJECT_SIZE:
+            case Mtp.Object.PROPERTY_PROTECTION_STATUS:
+                // protection status is always 0
+                outIntValue[0] = 0;
+                return MTP_RESPONSE_OK;
+            case Mtp.Object.PROPERTY_OBJECT_SIZE:
                 column = MtpObjects.ObjectColumns.SIZE;
                 break;
-            case MTP_PROPERTY_OBJECT_FILE_NAME:
+            case Mtp.Object.PROPERTY_OBJECT_FILE_NAME:
                 column = MtpObjects.ObjectColumns.DATA;
                 isString = true;
                 break;
-            case MTP_PROPERTY_DATE_MODIFIED:
+            case Mtp.Object.PROPERTY_DATE_MODIFIED:
                 column = MtpObjects.ObjectColumns.DATE_MODIFIED;
                 break;
-            case MTP_PROPERTY_PARENT_OBJECT:
+            case Mtp.Object.PROPERTY_PARENT_OBJECT:
                 column = MtpObjects.ObjectColumns.PARENT;
                 break;
+            case Mtp.Object.PROPERTY_PERSISTENT_UID:
+                // PUID is concatenation of storageID and object handle
+                long puid = mStorageID;
+                puid <<= 32;
+                puid += handle;
+                outIntValue[0] = puid;
+                return MTP_RESPONSE_OK;
             default:
                 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
         }
@@ -305,7 +308,7 @@
                     String value = c.getString(1);
                     int start = 0;
 
-                    if (property == MTP_PROPERTY_OBJECT_FILE_NAME) {
+                    if (property == Mtp.Object.PROPERTY_OBJECT_FILE_NAME) {
                         // extract name from full path
                         int lastSlash = value.lastIndexOf('/');
                         if (lastSlash >= 0) {
diff --git a/media/java/android/media/PresetReverb.java b/media/java/android/media/PresetReverb.java
index 1ec36fc..65175ff 100644
--- a/media/java/android/media/PresetReverb.java
+++ b/media/java/android/media/PresetReverb.java
@@ -71,31 +71,31 @@
     /**
      * No reverb or reflections
      */
-    public static final int PRESET_NONE        = 0;
+    public static final short PRESET_NONE        = 0;
     /**
      * Reverb preset representing a small room less than five meters in length
      */
-    public static final int PRESET_SMALLROOM   = 1;
+    public static final short PRESET_SMALLROOM   = 1;
     /**
      * Reverb preset representing a medium room with a length of ten meters or less
      */
-    public static final int PRESET_MEDIUMROOM  = 2;
+    public static final short PRESET_MEDIUMROOM  = 2;
     /**
      * Reverb preset representing a large-sized room suitable for live performances
      */
-    public static final int PRESET_LARGEROOM   = 3;
+    public static final short PRESET_LARGEROOM   = 3;
     /**
      * Reverb preset representing a medium-sized hall
      */
-    public static final int PRESET_MEDIUMHALL  = 4;
+    public static final short PRESET_MEDIUMHALL  = 4;
     /**
      * Reverb preset representing a large-sized hall suitable for a full orchestra
      */
-    public static final int PRESET_LARGEHALL   = 5;
+    public static final short PRESET_LARGEHALL   = 5;
     /**
      * Reverb preset representing a synthesis of the traditional plate reverb
      */
-    public static final int PRESET_PLATE       = 6;
+    public static final short PRESET_PLATE       = 6;
 
     /**
      * Registered listener for parameter changes.
diff --git a/media/java/android/media/Virtualizer.java b/media/java/android/media/Virtualizer.java
index d03c2a8..b08f36e 100644
--- a/media/java/android/media/Virtualizer.java
+++ b/media/java/android/media/Virtualizer.java
@@ -100,7 +100,7 @@
            UnsupportedOperationException, RuntimeException {
         super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
 
-        short[] value = new short[1];
+        int[] value = new int[1];
         checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
         mStrengthSupported = (value[0] != 0);
     }
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index abbea30..4ef3a9f 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -29,6 +29,7 @@
 
 #include "MtpDatabase.h"
 #include "MtpDataPacket.h"
+#include "MtpProperty.h"
 #include "MtpUtils.h"
 #include "mtp.h"
 
@@ -45,6 +46,7 @@
 static jmethodID method_getSupportedObjectProperties;
 static jmethodID method_getSupportedDeviceProperties;
 static jmethodID method_getObjectProperty;
+static jmethodID method_setObjectProperty;
 static jmethodID method_getObjectInfo;
 static jmethodID method_getObjectFilePath;
 static jmethodID method_deleteFile;
@@ -98,10 +100,22 @@
     virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format);
     virtual MtpDevicePropertyList*  getSupportedDeviceProperties();
 
-    virtual MtpResponseCode         getObjectProperty(MtpObjectHandle handle,
+    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
                                             MtpObjectProperty property,
                                             MtpDataPacket& packet);
 
+    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property);
+
     virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
                                             MtpDataPacket& packet);
 
@@ -116,8 +130,25 @@
 
     virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
                                             MtpObjectHandleList* references);
+
+    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format);
+
+    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property);
 };
 
+// ----------------------------------------------------------------------------
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        LOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
+// ----------------------------------------------------------------------------
+
 MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
     :   mDatabase(env->NewGlobalRef(client)),
         mIntBuffer(NULL),
@@ -165,8 +196,12 @@
                                             uint64_t size,
                                             time_t modified) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    return env->CallIntMethod(mDatabase, method_beginSendObject, env->NewStringUTF(path),
-                (jint)format, (jint)parent, (jint)storage, (jlong)size, (jlong)modified);
+    MtpObjectHandle result = env->CallIntMethod(mDatabase, method_beginSendObject,
+            env->NewStringUTF(path), (jint)format, (jint)parent, (jint)storage,
+            (jlong)size, (jlong)modified);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
 }
 
 void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle,
@@ -174,6 +209,8 @@
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(mDatabase, method_endSendObject, env->NewStringUTF(path),
                         (jint)handle, (jint)format, (jboolean)succeeded);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }
 
 MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
@@ -189,16 +226,21 @@
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
         list->push(handles[i]);
-   env->ReleaseIntArrayElements(array, handles, 0);
-   return list;
+    env->ReleaseIntArrayElements(array, handles, 0);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
 }
 
 int MyMtpDatabase::getNumObjects(MtpStorageID storageID,
                                 MtpObjectFormat format,
                                 MtpObjectHandle parent) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    return env->CallIntMethod(mDatabase, method_getNumObjects,
+    int result = env->CallIntMethod(mDatabase, method_getNumObjects,
                 (jint)storageID, (jint)format, (jint)parent);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
 }
 
 MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
@@ -212,8 +254,10 @@
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
         list->push(formats[i]);
-   env->ReleaseIntArrayElements(array, formats, 0);
-   return list;
+    env->ReleaseIntArrayElements(array, formats, 0);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
 }
 
 MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() {
@@ -227,8 +271,10 @@
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
         list->push(formats[i]);
-   env->ReleaseIntArrayElements(array, formats, 0);
-   return list;
+    env->ReleaseIntArrayElements(array, formats, 0);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
 }
 
 MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
@@ -242,8 +288,10 @@
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
         list->push(properties[i]);
-   env->ReleaseIntArrayElements(array, properties, 0);
-   return list;
+    env->ReleaseIntArrayElements(array, properties, 0);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
 }
 
 MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
@@ -257,11 +305,13 @@
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
         list->push(properties[i]);
-   env->ReleaseIntArrayElements(array, properties, 0);
-   return list;
+    env->ReleaseIntArrayElements(array, properties, 0);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
 }
 
-MtpResponseCode MyMtpDatabase::getObjectProperty(MtpObjectHandle handle,
+MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
                                             MtpObjectProperty property,
                                             MtpDataPacket& packet) {
     int         type;
@@ -279,6 +329,15 @@
     jlong longValue = longValues[0];
     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
 
+    // special case MTP_PROPERTY_DATE_MODIFIED, which is a string to MTP
+    // but stored internally as a uint64
+    if (property == MTP_PROPERTY_DATE_MODIFIED) {
+        char    date[20];
+        formatDateTime(longValue, date, sizeof(date));
+        packet.putString(date);
+        return MTP_RESPONSE_OK;
+    }
+
     switch (type) {
         case MTP_TYPE_INT8:
             packet.putInt8(longValue);
@@ -304,6 +363,12 @@
         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);
@@ -315,9 +380,31 @@
             LOGE("unsupported object type\n");
             return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     }
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return MTP_RESPONSE_OK;
 }
 
+MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet) {
+    return -1;
+}
+
+MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) {
+    return -1;
+}
+
+MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) {
+    return -1;
+}
+
+MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
+    return -1;
+}
+
 MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
                                             MtpDataPacket& packet) {
     char    date[20];
@@ -339,9 +426,10 @@
     uint64_t modified = longValues[1];
     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
 
-    int associationType = (format == MTP_FORMAT_ASSOCIATION ?
-                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
-                            MTP_ASSOCIATION_TYPE_UNDEFINED);
+//    int associationType = (format == MTP_FORMAT_ASSOCIATION ?
+//                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
+//                            MTP_ASSOCIATION_TYPE_UNDEFINED);
+    int associationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
 
     packet.putUInt32(storageID);
     packet.putUInt16(format);
@@ -368,6 +456,7 @@
     packet.putString(date);   // date modified
     packet.putEmptyString();   // keywords
 
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return MTP_RESPONSE_OK;
 }
 
@@ -388,12 +477,16 @@
     fileLength = longValues[0];
     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
     
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return result;
 }
 
 MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    return env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle);
+    MtpResponseCode result = env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
 }
 
 struct PropertyTableEntry {
@@ -433,11 +526,14 @@
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
         list->push(handles[i]);
-   env->ReleaseIntArrayElements(array, handles, 0);
-   return list;
+    env->ReleaseIntArrayElements(array, handles, 0);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
 }
 
-MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle, MtpObjectHandleList* references) {
+MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle,
+                                                    MtpObjectHandleList* references) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     int count = references->size();
     jintArray array = env->NewIntArray(count);
@@ -449,18 +545,42 @@
      for (int i = 0; i < count; i++)
         handles[i] = (*references)[i];
     env->ReleaseIntArrayElements(array, handles, 0);
-    return env->CallIntMethod(mDatabase, method_setObjectReferences,
+    MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences,
                 (jint)handle, array);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
 }
 
-// ----------------------------------------------------------------------------
-
-static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
-    if (env->ExceptionCheck()) {
-        LOGE("An exception was thrown by callback '%s'.", methodName);
-        LOGE_EX(env);
-        env->ExceptionClear();
+MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format) {
+    MtpProperty* result = NULL;
+    switch (property) {
+        case MTP_PROPERTY_OBJECT_FORMAT:
+        case MTP_PROPERTY_PROTECTION_STATUS:
+            result = new MtpProperty(property, MTP_TYPE_UINT16);
+            break;
+        case MTP_PROPERTY_STORAGE_ID:
+        case MTP_PROPERTY_PARENT_OBJECT:
+            result = new MtpProperty(property, MTP_TYPE_UINT32);
+            break;
+        case MTP_PROPERTY_OBJECT_SIZE:
+            result = new MtpProperty(property, MTP_TYPE_UINT64);
+            break;
+        case MTP_PROPERTY_PERSISTENT_UID:
+            result = new MtpProperty(property, MTP_TYPE_UINT128);
+            break;
+        case MTP_PROPERTY_OBJECT_FILE_NAME:
+        case MTP_PROPERTY_DATE_MODIFIED:
+            result = new MtpProperty(property, MTP_TYPE_STR);
+            break;
     }
+
+    return result;
+}
+
+MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
+    return NULL;
 }
 
 #endif // HAVE_ANDROID_OS
diff --git a/media/libeffects/lvm/lib/Android.mk b/media/libeffects/lvm/lib/Android.mk
index a944212..ff34707 100644
--- a/media/libeffects/lvm/lib/Android.mk
+++ b/media/libeffects/lvm/lib/Android.mk
@@ -122,3 +122,58 @@
     $(LOCAL_PATH)/StereoWidening/lib
 
 include $(BUILD_STATIC_LIBRARY)
+
+# Reverb library
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_SRC_FILES:= \
+    Reverb/src/LVREV_ApplyNewSettings.c \
+    Reverb/src/LVREV_ClearAudioBuffers.c \
+    Reverb/src/LVREV_GetControlParameters.c \
+    Reverb/src/LVREV_GetInstanceHandle.c \
+    Reverb/src/LVREV_GetMemoryTable.c \
+    Reverb/src/LVREV_Process.c \
+    Reverb/src/LVREV_SetControlParameters.c \
+    Reverb/src/LVREV_Tables.c \
+    Common/src/Abs_32.c \
+    Common/src/InstAlloc.c \
+    Common/src/LoadConst_16.c \
+    Common/src/LoadConst_32.c \
+    Common/src/From2iToMono_32.c \
+    Common/src/Mult3s_32x16.c \
+    Common/src/FO_1I_D32F32C31_TRC_WRA_01.c \
+    Common/src/FO_1I_D32F32Cll_TRC_WRA_01_Init.c \
+    Common/src/DelayAllPass_Sat_32x16To32.c \
+    Common/src/Copy_16.c \
+    Common/src/Mac3s_Sat_32x16.c \
+    Common/src/DelayWrite_32.c \
+    Common/src/Shift_Sat_v32xv32.c \
+    Common/src/Add2_Sat_32x32.c \
+    Common/src/JoinTo2i_32x32.c \
+    Common/src/MonoTo2I_32.c \
+    Common/src/LVM_FO_HPF.c \
+    Common/src/LVM_FO_LPF.c \
+    Common/src/LVM_Polynomial.c \
+    Common/src/LVM_Power10.c \
+    Common/src/LVM_GetOmega.c \
+    Common/src/MixSoft_2St_D32C31_SAT.c \
+    Common/src/MixSoft_1St_D32C31_WRA.c \
+    Common/src/MixInSoft_D32C31_SAT.c \
+    Common/src/LVM_Mixer_TimeConstant.c \
+    Common/src/Core_MixHard_2St_D32C31_SAT.c \
+    Common/src/Core_MixSoft_1St_D32C31_WRA.c \
+    Common/src/Core_MixInSoft_D32C31_SAT.c
+
+LOCAL_MODULE:= libreverb
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_C_INCLUDES += \
+    $(LOCAL_PATH)/Reverb/lib \
+    $(LOCAL_PATH)/Reverb/src \
+    $(LOCAL_PATH)/Common/lib \
+    $(LOCAL_PATH)/Common/src
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c
index 8cf84b7..4a9dc72 100755
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c
@@ -17,9 +17,9 @@
 
 /****************************************************************************************
 
-     $Author: nxp007753 $
-     $Revision: 1315 $
-     $Date: 2010-07-23 11:52:08 +0200 (Fri, 23 Jul 2010) $
+     $Author: beq06068 $
+     $Revision: 1401 $
+     $Date: 2010-08-03 09:52:22 +0200 (Tue, 03 Aug 2010) $
 
 *****************************************************************************************/
 
@@ -128,7 +128,7 @@
                  (void *)&pInstance->pData->HPFTaps,                /* Destination Cast to void: \
                                                                      no dereferencing in function*/
                  sizeof(pInstance->pData->HPFTaps)/sizeof(LVM_INT16));   /* Number of words */
-    BQ_2I_D32F32Cll_TRC_WRA_01_Init(&pInstance->pCoef->HPFInstance,      /* Initialise the filter */
+    BQ_2I_D32F32Cll_TRC_WRA_01_Init(&pInstance->pCoef->HPFInstance,         /* Initialise the filter */
                                     &pInstance->pData->HPFTaps,
                                     (BQ_C32_Coefs_t *)&LVDBE_HPF_Table[Offset]);
 
@@ -140,7 +140,7 @@
                  (void *)&pInstance->pData->BPFTaps,                /* Destination Cast to void:\
                                                                      no dereferencing in function*/
                  sizeof(pInstance->pData->BPFTaps)/sizeof(LVM_INT16));   /* Number of words */
-    BP_1I_D32F32Cll_TRC_WRA_02_Init(&pInstance->pCoef->BPFInstance,      /* Initialise the filter */
+    BP_1I_D32F32Cll_TRC_WRA_02_Init(&pInstance->pCoef->BPFInstance,         /* Initialise the filter */
                                     &pInstance->pData->BPFTaps,
                                     (BP_C32_Coefs_t *)&LVDBE_BPF_Table[Offset]);
 
@@ -317,6 +317,7 @@
 {
 
     LVDBE_Instance_t    *pInstance =(LVDBE_Instance_t  *)hInstance;
+    LVMixer3_2St_st     *pBypassMixer_Instance = &pInstance->pData->BypassMixer;
 
 
     /*
@@ -339,6 +340,14 @@
     {
         LVDBE_SetAGC(pInstance,                         /* Instance pointer */
                      pParams);                          /* New parameters */
+
+        LVC_Mixer_SetTimeConstant(&pBypassMixer_Instance->MixerStream[0],
+            LVDBE_BYPASS_MIXER_TC,pParams->SampleRate,2);
+
+        LVC_Mixer_SetTimeConstant(&pBypassMixer_Instance->MixerStream[1],
+            LVDBE_BYPASS_MIXER_TC,pParams->SampleRate,2);
+
+
     }
 
 
@@ -356,17 +365,13 @@
 
     if (pInstance->Params.OperatingMode==LVDBE_ON && pParams->OperatingMode==LVDBE_OFF)
     {
-        LVDBE_Params_t  Params  = *pParams;             /* make local copy of params */
-        Params.EffectLevel      = 0;                    /* zero effect level before switching off module*/
-        pInstance->bTransitionOnToOff  = LVM_TRUE;             /* Set the CallBack */
-        LVDBE_SetAGC(pInstance,                         /* Instance pointer */
-                     &Params);                          /* New parameters */
+        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[0],0);
+        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[1],0x00007FFF);
     }
     if (pInstance->Params.OperatingMode==LVDBE_OFF && pParams->OperatingMode==LVDBE_ON)
     {
-        pInstance->bTransitionOnToOff  = LVM_FALSE;     /* Set the CallBack */
-        LVDBE_SetAGC(pInstance,                         /* Instance pointer */
-                     pParams);                          /* New parameters */
+        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[0],0x00007FFF);
+        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[1],0);
     }
 
     /*
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c
index 75869c7..95c421d 100755
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c
@@ -17,9 +17,9 @@
 
 /****************************************************************************************
 
-     $Author: nxp007753 $
-     $Revision: 1081 $
-     $Date: 2010-07-05 11:48:44 +0200 (Mon, 05 Jul 2010) $
+     $Author: beq06068 $
+     $Revision: 1399 $
+     $Date: 2010-08-03 08:16:00 +0200 (Tue, 03 Aug 2010) $
 
 *****************************************************************************************/
 
@@ -160,6 +160,7 @@
 
     LVDBE_Instance_t      *pInstance;
     LVMixer3_1St_st       *pMixer_Instance;
+    LVMixer3_2St_st       *pBypassMixer_Instance;
     LVM_INT16             i;
     LVM_INT32             MixGain;
 
@@ -227,7 +228,7 @@
     /*
      * Initialise the filters
      */
-    LVDBE_SetFilters(pInstance,                                 /* Set the filter taps and coefficients */
+    LVDBE_SetFilters(pInstance,                 /* Set the filter taps and coefficients */
                      &pInstance->Params);
 
 
@@ -236,7 +237,8 @@
      */
     LVDBE_SetAGC(pInstance,                                     /* Set the AGC gain */
                  &pInstance->Params);
-    pInstance->pData->AGCInstance.AGC_Gain = pInstance->pData->AGCInstance.AGC_MaxGain;   /* Default to the bass boost setting */
+    pInstance->pData->AGCInstance.AGC_Gain = pInstance->pData->AGCInstance.AGC_MaxGain;
+                                                /* Default to the bass boost setting */
 
 
     /*
@@ -245,7 +247,8 @@
     LVDBE_SetVolume(pInstance,                                         /* Set the Volume */
                     &pInstance->Params);
 
-    pInstance->pData->AGCInstance.Volume = pInstance->pData->AGCInstance.Target;  /* Initialise as the target */
+    pInstance->pData->AGCInstance.Volume = pInstance->pData->AGCInstance.Target;
+                                                /* Initialise as the target */
 
     pMixer_Instance = &pInstance->pData->BypassVolume;
     MixGain = LVC_Mixer_GetTarget(&pMixer_Instance->MixerStream[0]);
@@ -258,9 +261,31 @@
     pMixer_Instance->MixerStream[0].CallbackSet = 0;
 
     /*
-     * Initialise the clicks minimisation variable
+     * Initialise the clicks minimisation BypassMixer
      */
-    pInstance->bTransitionOnToOff   =   LVM_FALSE;
+
+    pBypassMixer_Instance = &pInstance->pData->BypassMixer;
+
+    /*
+     * Setup the mixer gain for the processed path
+     */
+    pBypassMixer_Instance->MixerStream[0].CallbackParam = 0;
+    pBypassMixer_Instance->MixerStream[0].pCallbackHandle = LVM_NULL;
+    pBypassMixer_Instance->MixerStream[0].pCallBack = LVM_NULL;
+    pBypassMixer_Instance->MixerStream[0].CallbackSet=0;
+    LVC_Mixer_Init(&pBypassMixer_Instance->MixerStream[0],0,0);
+    LVC_Mixer_SetTimeConstant(&pBypassMixer_Instance->MixerStream[0],
+        LVDBE_BYPASS_MIXER_TC,pInstance->Params.SampleRate,2);
+    /*
+     * Setup the mixer gain for the unprocessed path
+     */
+    pBypassMixer_Instance->MixerStream[1].CallbackParam = 0;
+    pBypassMixer_Instance->MixerStream[1].pCallbackHandle = LVM_NULL;
+    pBypassMixer_Instance->MixerStream[1].pCallBack = LVM_NULL;
+    pBypassMixer_Instance->MixerStream[1].CallbackSet=0;
+    LVC_Mixer_Init(&pBypassMixer_Instance->MixerStream[1],0x00007FFF,0x00007FFF);
+    LVC_Mixer_SetTimeConstant(&pBypassMixer_Instance->MixerStream[1],
+        LVDBE_BYPASS_MIXER_TC,pInstance->Params.SampleRate,2);
 
     return(LVDBE_SUCCESS);
 }
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h b/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h
index 3e09cf4..df32873 100755
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h
@@ -17,9 +17,9 @@
 
 /****************************************************************************************
 
-     $Author: nxp007753 $
-     $Revision: 1081 $
-     $Date: 2010-07-05 11:48:44 +0200 (Mon, 05 Jul 2010) $
+     $Author: beq06068 $
+     $Revision: 1399 $
+     $Date: 2010-08-03 08:16:00 +0200 (Tue, 03 Aug 2010) $
 
 *****************************************************************************************/
 
@@ -75,6 +75,8 @@
 #define LVDBE_SCRATCHBUFFERS_INPLACE     4       /* Number of buffers required for inplace processing */
 
 #define LVDBE_MIXER_TC                   5       /* Mixer time  */
+#define LVDBE_BYPASS_MIXER_TC            100     /* Bypass mixer time */
+
 
 /****************************************************************************************/
 /*                                                                                      */
@@ -92,6 +94,7 @@
     Biquad_2I_Order2_Taps_t     HPFTaps;            /* High pass filter taps */
     Biquad_1I_Order2_Taps_t     BPFTaps;            /* Band pass filter taps */
     LVMixer3_1St_st             BypassVolume;       /* Bypass volume scaler */
+    LVMixer3_2St_st             BypassMixer;        /* Bypass Mixer for Click Removal */
 
 } LVDBE_Data_t;
 
@@ -115,9 +118,6 @@
     /* Data and coefficient pointers */
     LVDBE_Data_t                *pData;                /* Instance data */
     LVDBE_Coef_t                *pCoef;                /* Instance coefficients */
-
-    LVM_INT32                   bTransitionOnToOff;
-
 } LVDBE_Instance_t;
 
 
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Process.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Process.c
index 35eec07..04032c0 100755
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Process.c
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Process.c
@@ -17,9 +17,9 @@
 
 /****************************************************************************************
 
-     $Author: nxp007753 $
-     $Revision: 1081 $
-     $Date: 2010-07-05 11:48:44 +0200 (Mon, 05 Jul 2010) $
+     $Author: beq06068 $
+     $Revision: 1400 $
+     $Date: 2010-08-03 09:22:37 +0200 (Tue, 03 Aug 2010) $
 
 *****************************************************************************************/
 
@@ -89,10 +89,16 @@
 
     LVDBE_Instance_t    *pInstance =(LVDBE_Instance_t  *)hInstance;
     LVM_INT32           *pScratch  = (LVM_INT32 *)pInstance->MemoryTable.Region[LVDBE_MEMREGION_SCRATCH].pBaseAddress;
-    LVM_INT32           *pMono     = (LVM_INT32 *)pOutData;
+    LVM_INT32           *pMono;
     LVM_INT16           *pInput    = (LVM_INT16 *)pInData;
 
 
+    /* Scratch for Volume Control starts at offset of 2*NumSamples short values from pScratch */
+    LVM_INT16           *pScratchVol = (LVM_INT16 *)(&pScratch[NumSamples]);
+
+    /* Scratch for Mono path starts at offset of 2*NumSamples 32-bit values from pScratch */
+    pMono                            = &pScratch[2*NumSamples];
+
     /*
      * Check the number of samples is not too large
      */
@@ -104,18 +110,20 @@
     /*
      * Check if the algorithm is enabled
      */
-    if ((pInstance->Params.OperatingMode != LVDBE_OFF) ||
-        (pInstance->bTransitionOnToOff == LVM_TRUE))
+    /* DBE path is processed when DBE is ON or during On/Off transitions */
+    if ((pInstance->Params.OperatingMode == LVDBE_ON)||
+        (LVC_Mixer_GetCurrent(&pInstance->pData->BypassMixer.MixerStream[0])
+         !=LVC_Mixer_GetTarget(&pInstance->pData->BypassMixer.MixerStream[0])))
     {
 
         /*
          * Convert 16-bit samples to 32-bit and scale
          * (For a 16-bit implementation apply headroom loss here)
          */
-        Int16LShiftToInt32_16x32(pInput,                               /* Source 16-bit data      */
-                                 pScratch,                             /* Destination 32-bit data */
-                                 (LVM_INT16)(2*NumSamples),            /* Left and right          */
-                                 LVDBE_SCALESHIFT);                    /* Shift scale             */
+        Int16LShiftToInt32_16x32(pInput,                               /* Source 16-bit data    */
+                                 pScratch,                             /* Dest. 32-bit data     */
+                                 (LVM_INT16)(2*NumSamples),            /* Left and right        */
+                                 LVDBE_SCALESHIFT);                    /* Shift scale           */
 
 
         /*
@@ -123,61 +131,54 @@
          */
         if (pInstance->Params.HPFSelect == LVDBE_HPF_ON)
         {
-              BQ_2I_D32F32C30_TRC_WRA_01(&pInstance->pCoef->HPFInstance,    /* Filter instance         */
-                                       (LVM_INT32 *)pScratch,               /* Source                  */
-                                       (LVM_INT32 *)pScratch,               /* Destination             */
-                                       (LVM_INT16)NumSamples);              /* Number of samples       */
+              BQ_2I_D32F32C30_TRC_WRA_01(&pInstance->pCoef->HPFInstance,/* Filter instance      */
+                                       (LVM_INT32 *)pScratch,           /* Source               */
+                                       (LVM_INT32 *)pScratch,           /* Destination          */
+                                       (LVM_INT16)NumSamples);          /* Number of samples    */
         }
 
 
         /*
          * Create the mono stream
          */
-        From2iToMono_32(pScratch,                                      /* Stereo source           */
-                        pMono,                                         /* Mono destination        */
-                        (LVM_INT16)NumSamples);                        /* Number of samples       */
+        From2iToMono_32(pScratch,                                      /* Stereo source         */
+                        pMono,                                         /* Mono destination      */
+                        (LVM_INT16)NumSamples);                        /* Number of samples     */
 
 
         /*
          * Apply the band pass filter
          */
-          BP_1I_D32F32C30_TRC_WRA_02(&pInstance->pCoef->BPFInstance,     /* Filter instance         */
-                                   (LVM_INT32 *)pMono,                 /* Source                  */
-                                   (LVM_INT32 *)pMono,                 /* Destination             */
-                                   (LVM_INT16)NumSamples);             /* Number of samples       */
+        BP_1I_D32F32C30_TRC_WRA_02(&pInstance->pCoef->BPFInstance,     /* Filter instance       */
+                                   (LVM_INT32 *)pMono,                 /* Source                */
+                                   (LVM_INT32 *)pMono,                 /* Destination           */
+                                   (LVM_INT16)NumSamples);             /* Number of samples     */
 
 
         /*
          * Apply the AGC and mix
          */
-        AGC_MIX_VOL_2St1Mon_D32_WRA(&pInstance->pData->AGCInstance,    /* Instance pointer        */
-                                    pScratch,                          /* Stereo source           */
-                                    pMono,                             /* Mono band pass source   */
-                                    pScratch,                          /* Stereo destination      */
-                                    NumSamples);                       /* Number of samples       */
-
-        if(pInstance->bTransitionOnToOff == LVM_TRUE)
-        {
-            if ((pInstance->pData->AGCInstance.AGC_Gain == pInstance->pData->AGCInstance.AGC_Target)&&
-                (pInstance->pData->AGCInstance.AGC_Gain == 0))
-            {
-                    pInstance->bTransitionOnToOff = LVM_FALSE;
-            }
-        }
-
-
+        AGC_MIX_VOL_2St1Mon_D32_WRA(&pInstance->pData->AGCInstance,    /* Instance pointer      */
+                                    pScratch,                          /* Stereo source         */
+                                    pMono,                             /* Mono band pass source */
+                                    pScratch,                          /* Stereo destination    */
+                                    NumSamples);                       /* Number of samples     */
 
         /*
          * Convert 32-bit samples to 16-bit and saturate
          * (Not required for 16-bit implemenations)
          */
-        Int32RShiftToInt16_Sat_32x16(pScratch,                         /* Source 32-bit data      */
-                                     pOutData,                         /* Destination 16-bit data */
-                                     (LVM_INT16)(2*NumSamples),        /* Left and right          */
-                                     LVDBE_SCALESHIFT);                /* Shift scale             */
+        Int32RShiftToInt16_Sat_32x16(pScratch,                         /* Source 32-bit data    */
+                                     (LVM_INT16 *)pScratch,            /* Dest. 16-bit data     */
+                                     (LVM_INT16)(2*NumSamples),        /* Left and right        */
+                                     LVDBE_SCALESHIFT);                /* Shift scale           */
 
     }
-    else
+
+    /* Bypass Volume path is processed when DBE is OFF or during On/Off transitions */
+    if ((pInstance->Params.OperatingMode == LVDBE_OFF)||
+        (LVC_Mixer_GetCurrent(&pInstance->pData->BypassMixer.MixerStream[1])
+         !=LVC_Mixer_GetTarget(&pInstance->pData->BypassMixer.MixerStream[1])))
     {
 
         /*
@@ -186,11 +187,20 @@
          */
         LVC_MixSoft_1St_D16C31_SAT(&pInstance->pData->BypassVolume,
                                   pInData,
-                               pOutData,
-                               (LVM_INT16)(2*NumSamples));           /* Left and right           */
+                                  pScratchVol,
+                               (LVM_INT16)(2*NumSamples));           /* Left and right          */
 
     }
 
+    /*
+     * Mix DBE processed path and bypass volume path
+     */
+    LVC_MixSoft_2St_D16C31_SAT(&pInstance->pData->BypassMixer,
+                                    (LVM_INT16 *) pScratch,
+                                    pScratchVol,
+                                    pOutData,
+                                    (LVM_INT16)(2*NumSamples));
+
     return(LVDBE_SUCCESS);
 }
 
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c
index 922f77d..cec7501 100755
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c
@@ -18,8 +18,8 @@
 /****************************************************************************************
 
      $Author: nxp007753 $
-     $Revision: 1316 $
-     $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $
+     $Revision: 1331 $
+     $Date: 2010-07-27 12:26:23 +0200 (Tue, 27 Jul 2010) $
 
 *****************************************************************************************/
 
diff --git a/media/libeffects/lvm/lib/Reverb/lib/LVREV.h b/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
new file mode 100755
index 0000000..4c32db0
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*     Project::                                                        */
+/*     $Author: beq03888 $*/
+/*     $Revision: 1204 $*/
+/*     $Date: 2010-07-14 08:55:43 +0200 (Wed, 14 Jul 2010) $*/
+/*                                                                      */
+/************************************************************************/
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Header file for the application layer interface of the LVREV module                 */
+/*                                                                                      */
+/*  This files includes all definitions, types, structures and function prototypes      */
+/*  required by the calling layer. All other types, structures and functions are        */
+/*  private.                                                                            */
+/*                                                                                      */
+/****************************************************************************************/
+
+#ifndef __LVREV_H__
+#define __LVREV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Includes                                                                            */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVM_Types.h"
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Definitions                                                                         */
+/*                                                                                      */
+/****************************************************************************************/
+/* General */
+#define LVREV_BLOCKSIZE_MULTIPLE                1       /* Processing block size multiple */
+#define LVREV_MAX_T60                        7000       /* Maximum decay time is 7000ms */
+
+/* Memory table*/
+#define LVREV_NR_MEMORY_REGIONS                 4       /* Number of memory regions */
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Types                                                                               */
+/*                                                                                      */
+/****************************************************************************************/
+/* Instance handle */
+typedef void *LVREV_Handle_t;
+
+
+/* Status return values */
+typedef enum
+{
+    LVREV_SUCCESS            = 0,                       /* Successful return from a routine */
+    LVREV_NULLADDRESS        = 1,                       /* NULL allocation address */
+    LVREV_OUTOFRANGE         = 2,                       /* Out of range control parameter */
+    LVREV_INVALIDNUMSAMPLES  = 3,                       /* Invalid number of samples */
+    LVREV_RETURNSTATUS_DUMMY = LVM_MAXENUM
+} LVREV_ReturnStatus_en;
+
+
+/* Reverb delay lines */
+typedef enum
+{
+    LVREV_DELAYLINES_1     = 1,                         /* One delay line */
+    LVREV_DELAYLINES_2     = 2,                         /* Two delay lines */
+    LVREV_DELAYLINES_4     = 4,                         /* Four delay lines */
+    LVREV_DELAYLINES_DUMMY = LVM_MAXENUM
+} LVREV_NumDelayLines_en;
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Structures                                                                          */
+/*                                                                                      */
+/****************************************************************************************/
+
+/* Memory table containing the region definitions */
+typedef struct
+{
+    LVM_MemoryRegion_st        Region[LVREV_NR_MEMORY_REGIONS];  /* One definition for each region */
+} LVREV_MemoryTable_st;
+
+
+/* Control Parameter structure */
+typedef struct
+{
+    /* General parameters */
+    LVM_Mode_en                 OperatingMode;          /* Operating mode */
+    LVM_Fs_en                   SampleRate;             /* Sample rate */
+    LVM_Format_en               SourceFormat;           /* Source data format */
+
+    /* Parameters for REV */
+    LVM_UINT16                  Level;                  /* Level, 0 to 100 representing percentage of reverb */
+    LVM_UINT16                  LPF;                    /* Low pass filter, in Hz */
+    LVM_UINT16                  HPF;                    /* High pass filter, in Hz */
+    LVM_UINT16                  T60;                    /* Decay time constant, in ms */
+    LVM_UINT16                  Density;                /* Echo density, 0 to 100 for minimum to maximum density */
+    LVM_UINT16                  Damping;                /* Damping */
+    LVM_UINT16                  RoomSize;               /* Simulated room size, 1 to 100 for minimum to maximum size */
+
+} LVREV_ControlParams_st;
+
+
+/* Instance Parameter structure */
+typedef struct
+{
+    /* General */
+    LVM_UINT16                  MaxBlockSize;           /* Maximum processing block size */
+
+    /* Reverb */
+    LVM_Format_en               SourceFormat;           /* Source data formats to support */
+    LVREV_NumDelayLines_en      NumDelays;              /* The number of delay lines, 1, 2 or 4 */
+
+} LVREV_InstanceParams_st;
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Function Prototypes                                                                 */
+/*                                                                                      */
+/****************************************************************************************/
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_GetMemoryTable                                        */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  This function is used to obtain the LVREV module memory requirements to support     */
+/*  memory allocation. It can also be used to return the memory base address provided   */
+/*  during memory allocation to support freeing of memory when the LVREV module is no   */
+/*  longer required. It is called in two ways:                                          */
+/*                                                                                      */
+/*  hInstance = NULL                Returns the memory requirements                     */
+/*  hInstance = Instance handle     Returns the memory requirements and allocated       */
+/*                                  base addresses.                                     */
+/*                                                                                      */
+/*  When this function is called with hInstance = NULL the memory base address pointers */
+/*  will be NULL on return.                                                             */
+/*                                                                                      */
+/*  When the function is called for freeing memory, hInstance = Instance Handle the     */
+/*  memory table returns the allocated memory and base addresses used during            */
+/*  initialisation.                                                                     */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance Handle                                             */
+/*  pMemoryTable            Pointer to an empty memory table                            */
+/*  pInstanceParams         Pointer to the instance parameters                          */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_SUCCESS           Succeeded                                                   */
+/*  LVREV_NULLADDRESS       When pMemoryTable is NULL                                   */
+/*  LVREV_NULLADDRESS       When requesting memory requirements and pInstanceParams     */
+/*                          is NULL                                                     */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1.  This function may be interrupted by the LVREV_Process function                  */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_GetMemoryTable(LVREV_Handle_t           hInstance,
+                                           LVREV_MemoryTable_st     *pMemoryTable,
+                                           LVREV_InstanceParams_st  *pInstanceParams);
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_GetInstanceHandle                                     */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  This function is used to create a LVREV module instance. It returns the created     */
+/*  instance handle through phInstance. All parameters are set to invalid values, the   */
+/*  LVREV_SetControlParameters function must be called with a set of valid control      */
+/*  parameters before the LVREV_Process function can be called.                         */
+/*                                                                                      */
+/*  The memory allocation must be provided by the application by filling in the memory  */
+/*  region base addresses in the memory table before calling this function.             */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  phInstance              Pointer to the instance handle                              */
+/*  pMemoryTable            Pointer to the memory definition table                      */
+/*  pInstanceParams         Pointer to the instance parameters                          */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_SUCCESS           Succeeded                                                   */
+/*  LVREV_NULLADDRESS       When phInstance or pMemoryTable or pInstanceParams is NULL  */
+/*  LVREV_NULLADDRESS       When one of the memory regions has a NULL pointer           */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_GetInstanceHandle(LVREV_Handle_t            *phInstance,
+                                              LVREV_MemoryTable_st      *pMemoryTable,
+                                              LVREV_InstanceParams_st   *pInstanceParams);
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVXX_GetControlParameters                                   */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  Request the LVREV module control parameters. The current parameter set is returned  */
+/*  via the parameter pointer.                                                          */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance handle                                             */
+/*  pControlParams          Pointer to an empty parameter structure                     */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_SUCCESS           Succeeded                                                   */
+/*  LVREV_NULLADDRESS       When hInstance or pControlParams is NULL                    */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1.  This function may be interrupted by the LVREV_Process function                  */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_GetControlParameters(LVREV_Handle_t           hInstance,
+                                                 LVREV_ControlParams_st   *pControlParams);
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_SetControlParameters                                  */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  Sets or changes the LVREV module parameters.                                        */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance handle                                             */
+/*  pNewParams              Pointer to a parameter structure                            */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_SUCCESS           Succeeded                                                   */
+/*  LVREV_NULLADDRESS       When hInstance or pNewParams is NULL                        */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1.  This function may be interrupted by the LVREV_Process function                  */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_SetControlParameters(LVREV_Handle_t           hInstance,
+                                                 LVREV_ControlParams_st   *pNewParams);
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_ClearAudioBuffers                                     */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  This function is used to clear the internal audio buffers of the module.            */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance handle                                             */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_SUCCESS          Initialisation succeeded                                     */
+/*  LVREV_NULLADDRESS      Instance is NULL                                             */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1. This function must not be interrupted by the LVREV_Process function              */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_ClearAudioBuffers(LVREV_Handle_t  hInstance);
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_Process                                               */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  Process function for the LVREV module.                                              */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance handle                                             */
+/*  pInData                 Pointer to the input data                                   */
+/*  pOutData                Pointer to the output data                                  */
+/*  NumSamples              Number of samples in the input buffer                       */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_SUCCESS           Succeeded                                                   */
+/*  LVREV_INVALIDNUMSAMPLES NumSamples was larger than the maximum block size           */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1. The input and output buffers must be 32-bit aligned                              */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_Process(LVREV_Handle_t      hInstance,
+                                    const LVM_INT32     *pInData,
+                                    LVM_INT32           *pOutData,
+                                    const LVM_UINT16          NumSamples);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif      /* __LVREV_H__ */
+
+/* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.c
new file mode 100755
index 0000000..0026652
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_ApplyNewSettings.c
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/****************************************************************************************/
+/*                                                                                      */
+/*     Project::                                                                        */
+/*     $Author: nxp007753 $ */
+/*     $Revision: 1316 $ */
+/*     $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $ */
+/*                                                                                      */
+/****************************************************************************************/
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Includes                                                                            */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVREV_Private.h"
+#include "Filter.h"
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_ApplyNewSettings                                      */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  Applies the new control parameters                                                  */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  pPrivate                Pointer to the instance private parameters                  */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_Success           Succeeded                                                   */
+/*  LVREV_NULLADDRESS       When pPrivate is NULL                                       */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*                                                                                      */
+/****************************************************************************************/
+
+LVREV_ReturnStatus_en LVREV_ApplyNewSettings (LVREV_Instance_st     *pPrivate)
+{
+
+    LVM_Mode_en  OperatingMode;
+    LVM_INT32    NumberOfDelayLines;
+
+
+    /* Check for NULL pointer */
+    if(pPrivate == LVM_NULL)
+    {
+        return LVREV_NULLADDRESS;
+    }
+
+    OperatingMode = pPrivate->NewParams.OperatingMode;
+
+    if(pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_4)
+    {
+        NumberOfDelayLines = 4;
+    }
+    else if(pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_2)
+    {
+        NumberOfDelayLines = 2;
+    }
+    else
+    {
+        NumberOfDelayLines = 1;
+    }
+
+    /*
+     * Update the high pass filter coefficients
+     */
+    if((pPrivate->NewParams.HPF        != pPrivate->CurrentParams.HPF)        ||
+       (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
+       (pPrivate->bFirstControl        == LVM_TRUE))
+    {
+        LVM_INT32       Omega;
+        FO_C32_Coefs_t  Coeffs;
+
+        Omega = LVM_GetOmega(pPrivate->NewParams.HPF, pPrivate->NewParams.SampleRate);
+        LVM_FO_HPF(Omega, &Coeffs);
+        FO_1I_D32F32Cll_TRC_WRA_01_Init( &pPrivate->pFastCoef->HPCoefs, &pPrivate->pFastData->HPTaps, &Coeffs);
+        LoadConst_32(0,
+            (void *)&pPrivate->pFastData->HPTaps, /* Destination Cast to void: no dereferencing in function*/
+            sizeof(Biquad_1I_Order1_Taps_t)/sizeof(LVM_INT32));
+    }
+
+
+    /*
+     * Update the low pass filter coefficients
+     */
+    if((pPrivate->NewParams.LPF        != pPrivate->CurrentParams.LPF)        ||
+       (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
+       (pPrivate->bFirstControl        == LVM_TRUE))
+    {
+        LVM_INT32       Omega;
+        FO_C32_Coefs_t  Coeffs;
+
+
+        Coeffs.A0 = 0x7FFFFFFF;
+        Coeffs.A1 = 0;
+        Coeffs.B1 = 0;
+        if(pPrivate->NewParams.LPF <= (LVM_FsTable[pPrivate->NewParams.SampleRate] >> 1))
+        {
+            Omega = LVM_GetOmega(pPrivate->NewParams.LPF, pPrivate->NewParams.SampleRate);
+
+            /*
+             * Do not apply filter if w =2*pi*fc/fs >= 2.9
+             */
+            if(Omega<=LVREV_2_9_INQ29)
+            {
+                LVM_FO_LPF(Omega, &Coeffs);
+            }
+        }
+        FO_1I_D32F32Cll_TRC_WRA_01_Init( &pPrivate->pFastCoef->LPCoefs, &pPrivate->pFastData->LPTaps, &Coeffs);
+        LoadConst_32(0,
+            (void *)&pPrivate->pFastData->LPTaps,        /* Destination Cast to void: no dereferencing in function*/
+            sizeof(Biquad_1I_Order1_Taps_t)/sizeof(LVM_INT32));
+    }
+
+
+    /*
+     * Calculate the room size parameter
+     */
+    if( pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize)
+    {
+        /* Room size range is 10ms to 200ms
+         * 0%   -- 10ms
+         * 50%  -- 65ms
+         * 100% -- 120ms
+         */
+        pPrivate->RoomSizeInms = 10 + (((pPrivate->NewParams.RoomSize*11) + 5)/10);
+    }
+
+
+    /*
+     * Update the T delay number of samples and the all pass delay number of samples
+     */
+    if( (pPrivate->NewParams.RoomSize   != pPrivate->CurrentParams.RoomSize)   ||
+        (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
+        (pPrivate->bFirstControl        == LVM_TRUE))
+    {
+
+        LVM_UINT32  Temp;
+        LVM_INT32   APDelaySize;
+        LVM_INT32   Fs = LVM_GetFsFromTable(pPrivate->NewParams.SampleRate);
+        LVM_UINT32  DelayLengthSamples = (LVM_UINT32)(Fs * pPrivate->RoomSizeInms);
+        LVM_INT16   i;
+        LVM_INT16   ScaleTable[]  = {LVREV_T_3_Power_minus0_on_4, LVREV_T_3_Power_minus1_on_4, LVREV_T_3_Power_minus2_on_4, LVREV_T_3_Power_minus3_on_4};
+        LVM_INT16   MaxT_Delay[]  = {LVREV_MAX_T0_DELAY, LVREV_MAX_T1_DELAY, LVREV_MAX_T2_DELAY, LVREV_MAX_T3_DELAY};
+        LVM_INT16   MaxAP_Delay[] = {LVREV_MAX_AP0_DELAY, LVREV_MAX_AP1_DELAY, LVREV_MAX_AP2_DELAY, LVREV_MAX_AP3_DELAY};
+
+
+        /*
+         * For each delay line
+         */
+        for (i=0; i<NumberOfDelayLines; i++)
+        {
+            if (i != 0)
+            {
+                LVM_INT32 Temp1;  /* to avoid QAC warning on type conversion */
+                LVM_INT32 Temp2;  /* to avoid QAC warning on type conversion */
+
+                Temp2=(LVM_INT32)DelayLengthSamples;
+                MUL32x16INTO32(Temp2, ScaleTable[i], Temp1, 15)
+                Temp=(LVM_UINT32)Temp1;
+            }
+            else
+            {
+               Temp = DelayLengthSamples;
+            }
+            APDelaySize = Temp  / 1500;
+
+
+            /*
+             * Set the fixed delay
+             */
+            Temp                  = (MaxT_Delay[i] - MaxAP_Delay[i]) * Fs / 48000;
+            pPrivate->Delay_AP[i] = pPrivate->T[i] - Temp;
+
+
+            /*
+             * Set the tap selection
+             */
+            if (pPrivate->AB_Selection)
+            {
+                /* Smooth from tap A to tap B */
+                pPrivate->pOffsetB[i]             = &pPrivate->pDelay_T[i][pPrivate->T[i] - Temp - APDelaySize];
+                pPrivate->B_DelaySize[i]          = APDelaySize;
+                pPrivate->Mixer_APTaps[i].Target1 = 0;
+                pPrivate->Mixer_APTaps[i].Target2 = 0x7fffffff;
+            }
+            else
+            {
+                /* Smooth from tap B to tap A */
+                pPrivate->pOffsetA[i]             = &pPrivate->pDelay_T[i][pPrivate->T[i] - Temp - APDelaySize];
+                pPrivate->A_DelaySize[i]          = APDelaySize;
+                pPrivate->Mixer_APTaps[i].Target2 = 0;
+                pPrivate->Mixer_APTaps[i].Target1 = 0x7fffffff;
+            }
+
+            /*
+             * Set the maximum block size to the smallest delay size
+             */
+            pPrivate->MaxBlkLen   = Temp;
+            if (pPrivate->MaxBlkLen > pPrivate->A_DelaySize[i])
+            {
+                pPrivate->MaxBlkLen = pPrivate->A_DelaySize[i];
+            }
+            if (pPrivate->MaxBlkLen > pPrivate->B_DelaySize[i])
+            {
+                pPrivate->MaxBlkLen = pPrivate->B_DelaySize[i];
+            }
+        }
+        if (pPrivate->AB_Selection)
+        {
+            pPrivate->AB_Selection = 0;
+        }
+        else
+        {
+            pPrivate->AB_Selection = 1;
+        }
+
+
+        /*
+         * Limit the maximum block length
+         */
+        pPrivate->MaxBlkLen=pPrivate->MaxBlkLen-2;                                  /* Just as a precausion, but no problem if we remove this line      */
+        if(pPrivate->MaxBlkLen > pPrivate->InstanceParams.MaxBlockSize)
+        {
+            pPrivate->MaxBlkLen = (LVM_INT32)pPrivate->InstanceParams.MaxBlockSize;
+        }
+    }
+
+
+    /*
+     * Update the low pass filter coefficient
+     */
+    if( (pPrivate->NewParams.Damping    != pPrivate->CurrentParams.Damping)    ||
+        (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
+        (pPrivate->bFirstControl        == LVM_TRUE))
+    {
+
+        LVM_INT32       Temp;
+        LVM_INT32       Omega;
+        FO_C32_Coefs_t  Coeffs;
+        LVM_INT16       i;
+        LVM_INT16       Damping      = (LVM_INT16)((pPrivate->NewParams.Damping * 100) + 1000);
+        LVM_INT32       ScaleTable[] = {LVREV_T_3_Power_0_on_4, LVREV_T_3_Power_1_on_4, LVREV_T_3_Power_2_on_4, LVREV_T_3_Power_3_on_4};
+
+
+        /*
+         * For each filter
+         */
+        for (i=0; i<NumberOfDelayLines; i++)
+        {
+            if (i != 0)
+            {
+                MUL32x16INTO32(ScaleTable[i], Damping, Temp, 15)
+            }
+            else
+            {
+                Temp = Damping;
+            }
+            if(Temp <= (LVM_FsTable[pPrivate->NewParams.SampleRate] >> 1))
+            {
+                Omega = LVM_GetOmega((LVM_UINT16)Temp, pPrivate->NewParams.SampleRate);
+                LVM_FO_LPF(Omega, &Coeffs);
+            }
+            else
+            {
+                Coeffs.A0 = 0x7FF00000;
+                Coeffs.A1 = 0;
+                Coeffs.B1 = 0;
+            }
+            FO_1I_D32F32Cll_TRC_WRA_01_Init(&pPrivate->pFastCoef->RevLPCoefs[i], &pPrivate->pFastData->RevLPTaps[i], &Coeffs);
+        }
+    }
+
+
+    /*
+     * Update All-pass filter mixer time constants
+     */
+    if( (pPrivate->NewParams.RoomSize   != pPrivate->CurrentParams.RoomSize)   ||
+        (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
+        (pPrivate->NewParams.Density    != pPrivate->CurrentParams.Density))
+    {
+        LVM_INT16   i;
+        LVM_INT32   Alpha    = (LVM_INT32)LVM_Mixer_TimeConstant(LVREV_ALLPASS_TC, LVM_GetFsFromTable(pPrivate->NewParams.SampleRate), 1);
+        LVM_INT32   AlphaTap = (LVM_INT32)LVM_Mixer_TimeConstant(LVREV_ALLPASS_TAP_TC, LVM_GetFsFromTable(pPrivate->NewParams.SampleRate), 1);
+
+        for (i=0; i<4; i++)
+        {
+            pPrivate->Mixer_APTaps[i].Alpha1       = AlphaTap;
+            pPrivate->Mixer_APTaps[i].Alpha2       = AlphaTap;
+            pPrivate->Mixer_SGFeedback[i].Alpha    = Alpha;
+            pPrivate->Mixer_SGFeedforward[i].Alpha = Alpha;
+        }
+    }
+
+
+    /*
+     * Update the feed back gain
+     */
+    if( (pPrivate->NewParams.RoomSize   != pPrivate->CurrentParams.RoomSize)   ||
+        (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
+        (pPrivate->NewParams.T60        != pPrivate->CurrentParams.T60)        ||
+        (pPrivate->bFirstControl        == LVM_TRUE))
+    {
+
+        LVM_INT32               G[4];                       /* Feedback gain (Q7.24) */
+
+        if(pPrivate->NewParams.T60 == 0)
+        {
+            G[3] = 0;
+            G[2] = 0;
+            G[1] = 0;
+            G[0] = 0;
+        }
+        else
+        {
+            LVM_INT32   Temp1;
+            LVM_INT32   Temp2;
+            LVM_INT16   i;
+            LVM_INT16   ScaleTable[] = {LVREV_T_3_Power_minus0_on_4, LVREV_T_3_Power_minus1_on_4, LVREV_T_3_Power_minus2_on_4, LVREV_T_3_Power_minus3_on_4};
+
+
+            /*
+             * For each delay line
+             */
+            for (i=0; i<NumberOfDelayLines; i++)
+            {
+                Temp1 = (3 * pPrivate->RoomSizeInms * ScaleTable[i]) / pPrivate->NewParams.T60;
+                if(Temp1 >= (4 << 15))
+                {
+                    G[i] = 0;
+                }
+                else if((Temp1 >= (2 << 15)))
+                {
+                    Temp2 = LVM_Power10(-(Temp1 << 14));
+                    Temp1 = LVM_Power10(-(Temp1 << 14));
+                    MUL32x32INTO32(Temp1,Temp2,Temp1,24)
+                }
+                else
+                {
+                    Temp1 = LVM_Power10(-(Temp1 << 15));
+                }
+                if (NumberOfDelayLines == 1)
+                {
+                    G[i] = Temp1;
+                }
+                else
+                {
+                    LVM_INT32   TempG;
+                    MUL32x16INTO32(Temp1,ONE_OVER_SQRT_TWO,TempG,15)
+                    G[i]=TempG;
+                }
+            }
+        }
+
+        /* Set up the feedback mixers for four delay lines */
+        pPrivate->FeedbackMixer[0].Target=G[0]<<7;
+        pPrivate->FeedbackMixer[1].Target=G[1]<<7;
+        pPrivate->FeedbackMixer[2].Target=G[2]<<7;
+        pPrivate->FeedbackMixer[3].Target=G[3]<<7;
+    }
+
+
+    /*
+     * Calculate the gain correction
+     */
+    if((pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) ||
+       (pPrivate->NewParams.Level    != pPrivate->CurrentParams.Level)    ||
+       (pPrivate->NewParams.T60      != pPrivate->CurrentParams.T60) )
+    {
+        LVM_INT32 Index=0;
+        LVM_INT32 i=0;
+        LVM_INT32 Gain=0;
+        LVM_INT32 RoomSize=0;
+        LVM_INT32 T60;
+        LVM_INT32 Coefs[5];
+
+        if(pPrivate->NewParams.RoomSize==0)
+        {
+            RoomSize=1;
+        }
+        else
+        {
+            RoomSize=(LVM_INT32)pPrivate->NewParams.RoomSize;
+        }
+
+        if(pPrivate->NewParams.T60<100)
+        {
+            T60 = 100 * LVREV_T60_SCALE;
+        }
+        else
+        {
+            T60 = pPrivate->NewParams.T60 * LVREV_T60_SCALE;
+        }
+
+        /* Find the nearest room size in table */
+        for(i=0;i<24;i++)
+        {
+            if(RoomSize<= LVREV_GainPolyTable[i][0])
+            {
+                Index=i;
+                break;
+            }
+        }
+
+
+        if(RoomSize==LVREV_GainPolyTable[Index][0])
+        {
+            /* Take table values if the room size is in table */
+            for(i=1;i<5;i++)
+            {
+                Coefs[i-1]=LVREV_GainPolyTable[Index][i];
+            }
+            Coefs[4]=0;
+            Gain=LVM_Polynomial(3,Coefs,T60);       /* Q.24 result */
+        }
+        else
+        {
+            /* Interpolate the gain between nearest room sizes */
+
+            LVM_INT32 Gain1,Gain2;
+            LVM_INT32 Tot_Dist,Dist;
+
+            Tot_Dist=LVREV_GainPolyTable[Index][0]-LVREV_GainPolyTable[Index-1][0];
+            Dist=RoomSize-LVREV_GainPolyTable[Index-1][0];
+
+
+            /* Get gain for first */
+            for(i=1;i<5;i++)
+            {
+                Coefs[i-1]=LVREV_GainPolyTable[Index-1][i];
+            }
+            Coefs[4]=0;
+
+            Gain1=LVM_Polynomial(3,Coefs,T60);      /* Q.24 result */
+
+            /* Get gain for second */
+            for(i=1;i<5;i++)
+            {
+                Coefs[i-1]=LVREV_GainPolyTable[Index][i];
+            }
+            Coefs[4]=0;
+
+            Gain2=LVM_Polynomial(3,Coefs,T60);      /* Q.24 result */
+
+            /* Linear Interpolate the gain */
+            Gain = Gain1+ (((Gain2-Gain1)*Dist)/(Tot_Dist));
+        }
+
+
+        /*
+         * Get the inverse of gain: Q.15
+         * Gain is mostly above one except few cases, take only gains above 1
+         */
+        if(Gain < 16777216L)
+        {
+            pPrivate->Gain= 32767;
+        }
+        else
+        {
+            pPrivate->Gain=(LVM_INT16)(LVM_MAXINT_32/(Gain>>8));
+        }
+
+
+        Index=((32767*100)/(100+pPrivate->NewParams.Level));
+        pPrivate->Gain=(LVM_INT16)((pPrivate->Gain*Index)>>15);
+        pPrivate->GainMixer.Target = pPrivate->Gain*Index;
+    }
+
+
+    /*
+     * Update the all pass comb filter coefficient
+     */
+    if( (pPrivate->NewParams.Density != pPrivate->CurrentParams.Density) ||
+        (pPrivate->bFirstControl     == LVM_TRUE))
+    {
+        LVM_INT16   i;
+        LVM_INT32   b = pPrivate->NewParams.Density * LVREV_B_8_on_1000;
+
+        for (i=0;i<4; i++)
+        {
+            pPrivate->Mixer_SGFeedback[i].Target    = b;
+            pPrivate->Mixer_SGFeedforward[i].Target = b;
+        }
+    }
+
+
+    /*
+     * Update the bypass mixer time constant
+     */
+    if((pPrivate->NewParams.SampleRate   != pPrivate->CurrentParams.SampleRate)   ||
+       (pPrivate->NewParams.SourceFormat != pPrivate->CurrentParams.SourceFormat) ||
+       (pPrivate->bFirstControl          == LVM_TRUE))
+    {
+        LVM_UINT16   NumChannels = 1;                       /* Assume MONO format */
+        LVM_INT32    Alpha;
+
+        Alpha = (LVM_INT32)LVM_Mixer_TimeConstant(LVREV_FEEDBACKMIXER_TC, LVM_GetFsFromTable(pPrivate->NewParams.SampleRate), NumChannels);
+        pPrivate->FeedbackMixer[0].Alpha=Alpha;
+        pPrivate->FeedbackMixer[1].Alpha=Alpha;
+        pPrivate->FeedbackMixer[2].Alpha=Alpha;
+        pPrivate->FeedbackMixer[3].Alpha=Alpha;
+
+        if (pPrivate->NewParams.SourceFormat != LVM_MONO)
+        {
+            /* Stereo or Mono-in-Stereo format data */
+            NumChannels = 2;
+        }
+        pPrivate->BypassMixer.Alpha1 = (LVM_INT32)LVM_Mixer_TimeConstant(LVREV_BYPASSMIXER_TC, LVM_GetFsFromTable(pPrivate->NewParams.SampleRate), NumChannels);
+        pPrivate->BypassMixer.Alpha2 = pPrivate->BypassMixer.Alpha1;
+        pPrivate->GainMixer.Alpha    = pPrivate->BypassMixer.Alpha1;
+    }
+
+
+    /*
+     * Update the bypass mixer targets
+     */
+    if( (pPrivate->NewParams.Level != pPrivate->CurrentParams.Level) &&
+        (pPrivate->NewParams.OperatingMode == LVM_MODE_ON))
+    {
+        pPrivate->BypassMixer.Target2 = ((LVM_INT32)(pPrivate->NewParams.Level * 32767)/100)<<16;
+        pPrivate->BypassMixer.Target1 = LVREV_HEADROOM << 16;
+        if ((pPrivate->NewParams.Level == 0) && (pPrivate->bFirstControl == LVM_FALSE))
+        {
+            pPrivate->BypassMixer.CallbackSet2 = LVM_TRUE;
+        }
+        if (pPrivate->NewParams.Level != 0)
+        {
+            pPrivate->bDisableReverb = LVM_FALSE;
+        }
+    }
+
+    if(pPrivate->NewParams.OperatingMode != pPrivate->CurrentParams.OperatingMode)
+    {
+        if(pPrivate->NewParams.OperatingMode == LVM_MODE_ON)
+        {
+            pPrivate->BypassMixer.Target2 = ((LVM_INT32)(pPrivate->NewParams.Level * 32767)/100)<<16;
+            pPrivate->BypassMixer.Target1 = LVREV_HEADROOM << 16;
+
+            pPrivate->BypassMixer.CallbackSet2 = LVM_FALSE;
+            OperatingMode                      = LVM_MODE_ON;
+            if (pPrivate->NewParams.Level == 0)
+            {
+                pPrivate->bDisableReverb = LVM_TRUE;
+            }
+            else
+            {
+                pPrivate->bDisableReverb = LVM_FALSE;
+            }
+        }
+        else if (pPrivate->bFirstControl == LVM_FALSE)
+        {
+            pPrivate->BypassMixer.Target2 = 0x00000000;
+            pPrivate->BypassMixer.Target1 = 0x7FFFFFFF;
+            pPrivate->BypassMixer.CallbackSet2 = LVM_TRUE;
+            pPrivate->GainMixer.Target    = 0x03FFFFFF;
+            OperatingMode = LVM_MODE_ON;
+        }
+        else
+        {
+            OperatingMode = LVM_MODE_OFF;
+        }
+    }
+
+
+    /*
+     * If it is the first call to ApplyNew settings force the current to the target to begin immediate playback of the effect
+     */
+    if(pPrivate->bFirstControl == LVM_TRUE)
+    {
+        pPrivate->BypassMixer.Current1 = pPrivate->BypassMixer.Target1;
+        pPrivate->BypassMixer.Current2 = pPrivate->BypassMixer.Target2;
+    }
+
+
+    /*
+     * Copy the new parameters
+     */
+    pPrivate->CurrentParams = pPrivate->NewParams;
+    pPrivate->CurrentParams.OperatingMode = OperatingMode;
+
+
+    /*
+     * Update flag
+     */
+    if(pPrivate->bFirstControl == LVM_TRUE)
+    {
+        pPrivate->bFirstControl = LVM_FALSE;
+    }
+
+
+    return LVREV_SUCCESS;
+}
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                BypassMixer_Callback                                        */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  Controls the On to Off operating mode transition                                    */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  pPrivate                Pointer to the instance private parameters                  */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_Success           Succeeded                                                   */
+/*  LVREV_NULLADDRESS       When pPrivate is NULL                                       */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*                                                                                      */
+/****************************************************************************************/
+LVM_INT32 BypassMixer_Callback (void *pCallbackData,
+                                void *pGeneralPurpose,
+                                LVM_INT16 GeneralPurpose )
+{
+
+    LVREV_Instance_st     *pLVREV_Private = (LVREV_Instance_st *)pCallbackData;
+
+
+    /*
+     * Avoid build warnings
+     */
+    (void)pGeneralPurpose;
+    (void)GeneralPurpose;
+
+
+    /*
+     * Turn off
+     */
+    pLVREV_Private->CurrentParams.OperatingMode = LVM_MODE_OFF;
+    pLVREV_Private->bDisableReverb              = LVM_TRUE;
+    LVREV_ClearAudioBuffers((LVREV_Handle_t)pCallbackData);
+
+
+    return 0;
+}
+
+/* End of file */
+
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_ClearAudioBuffers.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_ClearAudioBuffers.c
new file mode 100755
index 0000000..b02b1a7
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_ClearAudioBuffers.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/****************************************************************************************/
+/*                                                                                      */
+/*     Project::                                                                        */
+/*     $Author: nxp007753 $ */
+/*     $Revision: 1316 $ */
+/*     $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $ */
+/*                                                                                      */
+/****************************************************************************************/
+
+/****************************************************************************************/
+/*                                                                                      */
+/* Includes                                                                             */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVREV_Private.h"
+#include "VectorArithmetic.h"
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_ClearAudioBuffers                                     */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  This function is used to clear the internal audio buffers of the module.            */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance handle                                             */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_SUCCESS          Initialisation succeeded                                     */
+/*  LVREV_NULLADDRESS      Instance is NULL                                             */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1. This function must not be interrupted by the LVM_Process function                */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_ClearAudioBuffers(LVREV_Handle_t  hInstance)
+{
+
+   LVREV_Instance_st     *pLVREV_Private = (LVREV_Instance_st *)hInstance;
+
+
+    /*
+     * Check for error conditions
+     */
+    /* Check for NULL pointers */
+    if(hInstance == LVM_NULL)
+    {
+        return LVREV_NULLADDRESS;
+    }
+
+    /*
+     * Clear all filter tap data, delay-lines and other signal related data
+     */
+
+
+    LoadConst_32(0,
+        (void *)&pLVREV_Private->pFastData->HPTaps, /* Destination Cast to void: no dereferencing in function*/
+        2);
+    LoadConst_32(0,
+        (void *)&pLVREV_Private->pFastData->LPTaps, /* Destination Cast to void: no dereferencing in function*/
+        2);
+
+    if((LVM_UINT16)pLVREV_Private->InstanceParams.NumDelays == LVREV_DELAYLINES_4)
+    {
+        LoadConst_32(0, (LVM_INT32 *)&pLVREV_Private->pFastData->RevLPTaps[3], 2);
+        LoadConst_32(0, (LVM_INT32 *)&pLVREV_Private->pFastData->RevLPTaps[2], 2);
+        LoadConst_32(0, (LVM_INT32 *)&pLVREV_Private->pFastData->RevLPTaps[1], 2);
+        LoadConst_32(0, (LVM_INT32 *)&pLVREV_Private->pFastData->RevLPTaps[0], 2);
+
+        LoadConst_32(0,pLVREV_Private->pDelay_T[3], (LVM_INT16)LVREV_MAX_T3_DELAY);
+        LoadConst_32(0,pLVREV_Private->pDelay_T[2], (LVM_INT16)LVREV_MAX_T2_DELAY);
+        LoadConst_32(0,pLVREV_Private->pDelay_T[1], (LVM_INT16)LVREV_MAX_T1_DELAY);
+        LoadConst_32(0,pLVREV_Private->pDelay_T[0], (LVM_INT16)LVREV_MAX_T0_DELAY);
+
+    }
+
+    if((LVM_UINT16)pLVREV_Private->InstanceParams.NumDelays >= LVREV_DELAYLINES_2)
+    {
+        LoadConst_32(0, (LVM_INT32 *)&pLVREV_Private->pFastData->RevLPTaps[1], 2);
+        LoadConst_32(0, (LVM_INT32 *)&pLVREV_Private->pFastData->RevLPTaps[0], 2);
+
+        LoadConst_32(0,pLVREV_Private->pDelay_T[1], (LVM_INT16)LVREV_MAX_T1_DELAY);
+        LoadConst_32(0,pLVREV_Private->pDelay_T[0], (LVM_INT16)LVREV_MAX_T0_DELAY);
+    }
+
+    if((LVM_UINT16)pLVREV_Private->InstanceParams.NumDelays >= LVREV_DELAYLINES_1)
+    {
+        LoadConst_32(0, (LVM_INT32 *)&pLVREV_Private->pFastData->RevLPTaps[0], 2);
+        LoadConst_32(0,pLVREV_Private->pDelay_T[0], (LVM_INT16)LVREV_MAX_T0_DELAY);
+    }
+
+    return LVREV_SUCCESS;
+}
+
+/* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetControlParameters.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetControlParameters.c
new file mode 100755
index 0000000..ebf145e
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetControlParameters.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/****************************************************************************************/
+/*                                                                                      */
+/*     Project::                                                                        */
+/*     $Author: nxp27078 $*/
+/*     $Revision: 762 $*/
+/*     $Date: 2010-06-11 14:50:33 +0200 (Fri, 11 Jun 2010) $*/
+/*                                                                                      */
+/****************************************************************************************/
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Includes                                                                            */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVREV_Private.h"
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_GetControlParameters                                  */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  Request the LVREV module control parameters. The current parameter set is returned  */
+/*  via the parameter pointer.                                                          */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance handle                                             */
+/*  pControlParams          Pointer to an empty parameter structure                     */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_Success           Succeeded                                                   */
+/*  LVREV_NULLADDRESS       When hInstance or pControlParams is NULL                    */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1.  This function may be interrupted by the LVREV_Process function                  */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_GetControlParameters(LVREV_Handle_t           hInstance,
+                                                 LVREV_ControlParams_st   *pControlParams)
+{
+
+    LVREV_Instance_st  *pLVREV_Private = (LVREV_Instance_st *)hInstance;
+
+
+    /*
+     * Check for error conditions
+     */
+    if((hInstance == LVM_NULL) || (pControlParams == LVM_NULL))
+    {
+        return LVREV_NULLADDRESS;
+    }
+
+    /*
+     * Return the current parameters
+     */
+    *pControlParams = pLVREV_Private->NewParams;
+
+    return LVREV_SUCCESS;
+}
+
+/* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.c
new file mode 100755
index 0000000..ca1c84d
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/****************************************************************************************/
+/*                                                                                      */
+/*     Project::                                                                        */
+/*     $Author: nxp007753 $ */
+/*     $Revision: 1316 $ */
+/*     $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $ */
+/*                                                                                      */
+/****************************************************************************************/
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Includes                                                                            */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVREV_Private.h"
+#include "InstAlloc.h"
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_GetInstanceHandle                                     */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  This function is used to create a LVREV module instance. It returns the created     */
+/*  instance handle through phInstance. All parameters are set to their default,        */
+/*  inactive state.                                                                     */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  phInstance              pointer to the instance handle                              */
+/*  pMemoryTable            Pointer to the memory definition table                      */
+/*  pInstanceParams         Pointer to the instance parameters                          */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_SUCCESS           Succeeded                                                   */
+/*  LVREV_NULLADDRESS       When phInstance or pMemoryTable or pInstanceParams is NULL  */
+/*  LVREV_NULLADDRESS       When one of the memory regions has a NULL pointer           */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_GetInstanceHandle(LVREV_Handle_t            *phInstance,
+                                              LVREV_MemoryTable_st      *pMemoryTable,
+                                              LVREV_InstanceParams_st   *pInstanceParams)
+{
+
+    INST_ALLOC              SlowData;
+    INST_ALLOC              FastData;
+    INST_ALLOC              FastCoef;
+    INST_ALLOC              Temporary;
+    LVREV_Instance_st       *pLVREV_Private;
+    LVM_INT16               i;
+    LVM_UINT16              MaxBlockSize;
+
+
+    /*
+     * Check for error conditions
+     */
+    /* Check for NULL pointers */
+    if((phInstance == LVM_NULL) || (pMemoryTable == LVM_NULL) || (pInstanceParams == LVM_NULL))
+    {
+        return LVREV_NULLADDRESS;
+    }
+    /* Check the memory table for NULL pointers */
+    for (i = 0; i < LVREV_NR_MEMORY_REGIONS; i++)
+    {
+        if (pMemoryTable->Region[i].Size!=0)
+        {
+            if (pMemoryTable->Region[i].pBaseAddress==LVM_NULL)
+            {
+                return(LVREV_NULLADDRESS);
+            }
+        }
+    }
+
+    /*
+     * Check all instance parameters are in range
+     */
+    /* Check for a non-zero block size */
+    if (pInstanceParams->MaxBlockSize == 0)
+    {
+        return LVREV_OUTOFRANGE;
+    }
+
+    /* Check for a valid number of delay lines */
+    if ((pInstanceParams->NumDelays != LVREV_DELAYLINES_1)&&
+        (pInstanceParams->NumDelays != LVREV_DELAYLINES_2)&&
+        (pInstanceParams->NumDelays != LVREV_DELAYLINES_4))
+    {
+        return LVREV_OUTOFRANGE;
+    }
+
+    /*
+     * Initialise the InstAlloc instances
+     */
+    InstAlloc_Init(&SlowData,  pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress);
+    InstAlloc_Init(&FastData,  pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress);
+    InstAlloc_Init(&FastCoef,  pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress);
+    InstAlloc_Init(&Temporary, pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress);
+
+    /*
+     * Zero all memory regions
+     */
+     LoadConst_16(0, (LVM_INT16 *)pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress, (LVM_INT16)((pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Size)/sizeof(LVM_INT16)));
+     LoadConst_16(0, (LVM_INT16 *)pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress, (LVM_INT16)((pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Size)/sizeof(LVM_INT16)));
+     LoadConst_16(0, (LVM_INT16 *)pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress, (LVM_INT16)((pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Size)/sizeof(LVM_INT16)));
+     LoadConst_16(0, (LVM_INT16 *)pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress, (LVM_INT16)((pMemoryTable->Region[LVM_TEMPORARY_FAST].Size)/sizeof(LVM_INT16)));
+
+    /*
+     * Set the instance handle if not already initialised
+     */
+    if (*phInstance == LVM_NULL)
+    {
+        *phInstance = InstAlloc_AddMember(&SlowData, sizeof(LVREV_Instance_st));
+    }
+    pLVREV_Private              =(LVREV_Instance_st *)*phInstance;
+    pLVREV_Private->MemoryTable = *pMemoryTable;
+
+    if(pInstanceParams->NumDelays ==LVREV_DELAYLINES_4)
+    {
+        MaxBlockSize = LVREV_MAX_AP3_DELAY;
+    }
+    else if(pInstanceParams->NumDelays ==LVREV_DELAYLINES_2)
+    {
+        MaxBlockSize = LVREV_MAX_AP1_DELAY;
+    }
+    else
+    {
+        MaxBlockSize = LVREV_MAX_AP0_DELAY;
+    }
+
+    if(MaxBlockSize>pInstanceParams->MaxBlockSize)
+    {
+        MaxBlockSize=pInstanceParams->MaxBlockSize;
+    }
+
+
+    /*
+     * Set the data, coefficient and temporary memory pointers
+     */
+    pLVREV_Private->pFastData = InstAlloc_AddMember(&FastData, sizeof(LVREV_FastData_st));                              /* Fast data memory base address */
+
+    if(pInstanceParams->NumDelays == LVREV_DELAYLINES_4)
+    {
+        pLVREV_Private->pDelay_T[3]     = InstAlloc_AddMember(&FastData, LVREV_MAX_T3_DELAY  * sizeof(LVM_INT32));
+        pLVREV_Private->pDelay_T[2]     = InstAlloc_AddMember(&FastData, LVREV_MAX_T2_DELAY  * sizeof(LVM_INT32));
+        pLVREV_Private->pDelay_T[1]     = InstAlloc_AddMember(&FastData, LVREV_MAX_T1_DELAY * sizeof(LVM_INT32));
+        pLVREV_Private->pDelay_T[0]     = InstAlloc_AddMember(&FastData, LVREV_MAX_T0_DELAY * sizeof(LVM_INT32));
+
+        for( i = 0; i < 4; i++)
+        {
+            pLVREV_Private->pScratchDelayLine[i] = InstAlloc_AddMember(&Temporary, sizeof(LVM_INT32) * MaxBlockSize);       /* Scratch for each delay line output */
+        }
+
+        LoadConst_32(0,pLVREV_Private->pDelay_T[3]  ,(LVM_INT16)LVREV_MAX_T3_DELAY);
+        LoadConst_32(0,pLVREV_Private->pDelay_T[2]  ,(LVM_INT16)LVREV_MAX_T2_DELAY);
+        LoadConst_32(0,pLVREV_Private->pDelay_T[1]  ,(LVM_INT16)LVREV_MAX_T1_DELAY);
+        LoadConst_32(0,pLVREV_Private->pDelay_T[0]  ,(LVM_INT16)LVREV_MAX_T0_DELAY);
+    }
+
+    if(pInstanceParams->NumDelays == LVREV_DELAYLINES_2)
+    {
+        pLVREV_Private->pDelay_T[1]  = InstAlloc_AddMember(&FastData, LVREV_MAX_T1_DELAY * sizeof(LVM_INT32));
+        pLVREV_Private->pDelay_T[0]  = InstAlloc_AddMember(&FastData, LVREV_MAX_T0_DELAY * sizeof(LVM_INT32));
+
+        for( i = 0; i < 2; i++)
+        {
+            pLVREV_Private->pScratchDelayLine[i] = InstAlloc_AddMember(&Temporary, sizeof(LVM_INT32) * MaxBlockSize);       /* Scratch for each delay line output */
+        }
+
+        LoadConst_32(0,pLVREV_Private->pDelay_T[1] , (LVM_INT16)LVREV_MAX_T1_DELAY);
+        LoadConst_32(0,pLVREV_Private->pDelay_T[0] , (LVM_INT16)LVREV_MAX_T0_DELAY);
+    }
+
+    if(pInstanceParams->NumDelays == LVREV_DELAYLINES_1)
+    {
+        pLVREV_Private->pDelay_T[0]  = InstAlloc_AddMember(&FastData, LVREV_MAX_T0_DELAY * sizeof(LVM_INT32));
+
+        for( i = 0; i < 1; i++)
+        {
+            pLVREV_Private->pScratchDelayLine[i] = InstAlloc_AddMember(&Temporary, sizeof(LVM_INT32) * MaxBlockSize);       /* Scratch for each delay line output */
+        }
+
+        LoadConst_32(0,pLVREV_Private->pDelay_T[0]  , (LVM_INT16)LVREV_MAX_T0_DELAY);
+    }
+
+    /* All-pass delay buffer addresses and sizes */
+    pLVREV_Private->T[0]         = LVREV_MAX_T0_DELAY;
+    pLVREV_Private->T[1]         = LVREV_MAX_T1_DELAY;
+    pLVREV_Private->T[2]         = LVREV_MAX_T2_DELAY;
+    pLVREV_Private->T[3]         = LVREV_MAX_T3_DELAY;
+    pLVREV_Private->AB_Selection = 1;       /* Select smoothing A to B */
+
+
+    pLVREV_Private->pFastCoef       = InstAlloc_AddMember(&FastCoef, sizeof(LVREV_FastCoef_st));                        /* Fast coefficient memory base address */
+    pLVREV_Private->pScratch        = InstAlloc_AddMember(&Temporary, sizeof(LVM_INT32) * MaxBlockSize);                /* General purpose scratch */
+    pLVREV_Private->pInputSave      = InstAlloc_AddMember(&Temporary, 2 * sizeof(LVM_INT32) * MaxBlockSize);            /* Mono->stereo input save for end mix */
+    LoadConst_32(0, pLVREV_Private->pInputSave, (LVM_INT16)(MaxBlockSize*2));
+
+
+    /*
+     * Save the instance parameters in the instance structure
+     */
+    pLVREV_Private->InstanceParams = *pInstanceParams;
+
+
+    /*
+     * Set the parameters to invalid
+     */
+    pLVREV_Private->CurrentParams.SampleRate    = LVM_FS_INVALID;
+    pLVREV_Private->CurrentParams.OperatingMode = LVM_MODE_DUMMY;
+    pLVREV_Private->CurrentParams.SourceFormat  = LVM_SOURCE_DUMMY;
+
+    pLVREV_Private->bControlPending             = LVM_FALSE;
+    pLVREV_Private->bFirstControl               = LVM_TRUE;
+    pLVREV_Private->bDisableReverb              = LVM_FALSE;
+
+
+    /*
+     * Set mixer parameters
+     */
+    pLVREV_Private->BypassMixer.CallbackParam2      = 0;
+    pLVREV_Private->BypassMixer.pCallbackHandle2    = pLVREV_Private;
+    pLVREV_Private->BypassMixer.pGeneralPurpose2    = LVM_NULL;
+    pLVREV_Private->BypassMixer.pCallBack2          = BypassMixer_Callback;
+    pLVREV_Private->BypassMixer.CallbackSet2        = LVM_FALSE;
+    pLVREV_Private->BypassMixer.Current2            = 0;
+    pLVREV_Private->BypassMixer.Target2             = 0;
+    pLVREV_Private->BypassMixer.CallbackParam1      = 0;
+    pLVREV_Private->BypassMixer.pCallbackHandle1    = LVM_NULL;
+    pLVREV_Private->BypassMixer.pGeneralPurpose1    = LVM_NULL;
+    pLVREV_Private->BypassMixer.pCallBack1          = LVM_NULL;
+    pLVREV_Private->BypassMixer.CallbackSet1        = LVM_FALSE;
+    pLVREV_Private->BypassMixer.Current1            = 0x7fffffff;
+    pLVREV_Private->BypassMixer.Target1             = 0x7fffffff;
+
+    pLVREV_Private->RoomSizeInms                    = 100;  // 100 msec
+
+
+    /*
+     *  Set the output gain mixer parameters
+     */
+    pLVREV_Private->GainMixer.CallbackParam      = 0;
+    pLVREV_Private->GainMixer.pCallbackHandle    = LVM_NULL;
+    pLVREV_Private->GainMixer.pGeneralPurpose    = LVM_NULL;
+    pLVREV_Private->GainMixer.pCallBack          = LVM_NULL;
+    pLVREV_Private->GainMixer.CallbackSet        = LVM_FALSE;
+    pLVREV_Private->GainMixer.Current            = 0x03ffffff;
+    pLVREV_Private->GainMixer.Target             = 0x03ffffff;
+
+
+    /*
+     * Set the All-Pass Filter mixers
+     */
+    for (i=0; i<4; i++)
+    {
+        pLVREV_Private->pOffsetA[i] = pLVREV_Private->pDelay_T[i];
+        pLVREV_Private->pOffsetB[i] = pLVREV_Private->pDelay_T[i];
+        /* Delay tap selection mixer */
+        pLVREV_Private->Mixer_APTaps[i].CallbackParam2   = 0;
+        pLVREV_Private->Mixer_APTaps[i].pCallbackHandle2 = LVM_NULL;
+        pLVREV_Private->Mixer_APTaps[i].pGeneralPurpose2 = LVM_NULL;
+        pLVREV_Private->Mixer_APTaps[i].pCallBack2       = LVM_NULL;
+        pLVREV_Private->Mixer_APTaps[i].CallbackSet2     = LVM_FALSE;
+        pLVREV_Private->Mixer_APTaps[i].Current2         = 0;
+        pLVREV_Private->Mixer_APTaps[i].Target2          = 0;
+        pLVREV_Private->Mixer_APTaps[i].CallbackParam1   = 0;
+        pLVREV_Private->Mixer_APTaps[i].pCallbackHandle1 = LVM_NULL;
+        pLVREV_Private->Mixer_APTaps[i].pGeneralPurpose1 = LVM_NULL;
+        pLVREV_Private->Mixer_APTaps[i].pCallBack1       = LVM_NULL;
+        pLVREV_Private->Mixer_APTaps[i].CallbackSet1     = LVM_FALSE;
+        pLVREV_Private->Mixer_APTaps[i].Current1         = 0;
+        pLVREV_Private->Mixer_APTaps[i].Target1          = 0x7fffffff;
+        /* Feedforward mixer */
+        pLVREV_Private->Mixer_SGFeedforward[i].CallbackParam   = 0;
+        pLVREV_Private->Mixer_SGFeedforward[i].pCallbackHandle = LVM_NULL;
+        pLVREV_Private->Mixer_SGFeedforward[i].pGeneralPurpose = LVM_NULL;
+        pLVREV_Private->Mixer_SGFeedforward[i].pCallBack       = LVM_NULL;
+        pLVREV_Private->Mixer_SGFeedforward[i].CallbackSet     = LVM_FALSE;
+        pLVREV_Private->Mixer_SGFeedforward[i].Current         = 0;
+        pLVREV_Private->Mixer_SGFeedforward[i].Target          = 0;
+        /* Feedback mixer */
+        pLVREV_Private->Mixer_SGFeedback[i].CallbackParam   = 0;
+        pLVREV_Private->Mixer_SGFeedback[i].pCallbackHandle = LVM_NULL;
+        pLVREV_Private->Mixer_SGFeedback[i].pGeneralPurpose = LVM_NULL;
+        pLVREV_Private->Mixer_SGFeedback[i].pCallBack       = LVM_NULL;
+        pLVREV_Private->Mixer_SGFeedback[i].CallbackSet     = LVM_FALSE;
+        pLVREV_Private->Mixer_SGFeedback[i].Current         = 0;
+        pLVREV_Private->Mixer_SGFeedback[i].Target          = 0;
+        /* Feedback gain mixer */
+        pLVREV_Private->FeedbackMixer[i].CallbackParam    = 0;
+        pLVREV_Private->FeedbackMixer[i].pCallbackHandle  = LVM_NULL;
+        pLVREV_Private->FeedbackMixer[i].pGeneralPurpose  = LVM_NULL;
+        pLVREV_Private->FeedbackMixer[i].pCallBack        = LVM_NULL;
+        pLVREV_Private->FeedbackMixer[i].CallbackSet      = LVM_FALSE;
+        pLVREV_Private->FeedbackMixer[i].Current          = 0;
+        pLVREV_Private->FeedbackMixer[i].Target           = 0;
+    }
+    /* Delay tap index */
+    pLVREV_Private->A_DelaySize[0] = LVREV_MAX_AP0_DELAY;
+    pLVREV_Private->B_DelaySize[0] = LVREV_MAX_AP0_DELAY;
+    pLVREV_Private->A_DelaySize[1] = LVREV_MAX_AP1_DELAY;
+    pLVREV_Private->B_DelaySize[1] = LVREV_MAX_AP1_DELAY;
+    pLVREV_Private->A_DelaySize[2] = LVREV_MAX_AP2_DELAY;
+    pLVREV_Private->B_DelaySize[2] = LVREV_MAX_AP2_DELAY;
+    pLVREV_Private->A_DelaySize[3] = LVREV_MAX_AP3_DELAY;
+    pLVREV_Private->B_DelaySize[3] = LVREV_MAX_AP3_DELAY;
+
+
+    LVREV_ClearAudioBuffers(*phInstance);
+
+    return LVREV_SUCCESS;
+}
+
+/* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.c
new file mode 100755
index 0000000..e16a3d3
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/****************************************************************************************/
+/*                                                                                      */
+/*     Project::                                                                        */
+/*     $Author: beq07716 $*/
+/*     $Revision: 1007 $*/
+/*     $Date: 2010-06-28 14:06:36 +0200 (Mon, 28 Jun 2010) $*/
+/*                                                                                      */
+/****************************************************************************************/
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Includes                                                                            */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVREV_Private.h"
+#include "InstAlloc.h"
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_GetMemoryTable                                        */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  This function is used for memory allocation and free. It can be called in           */
+/*  two ways:                                                                           */
+/*                                                                                      */
+/*  hInstance = NULL                Returns the memory requirements                     */
+/*  hInstance = Instance handle     Returns the memory requirements and allocated       */
+/*                                  base addresses.                                     */
+/*                                                                                      */
+/*  When this function is called for memory allocation (hInstance=NULL) the memory      */
+/*  base address pointers are NULL on return.                                           */
+/*                                                                                      */
+/*  When the function is called for free (hInstance = Instance Handle) the memory       */
+/*  table returns the allocated memory and base addresses used during initialisation.   */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance Handle                                             */
+/*  pMemoryTable            Pointer to an empty memory table                            */
+/*  pInstanceParams         Pointer to the instance parameters                          */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_Success           Succeeded                                                   */
+/*  LVREV_NULLADDRESS       When pMemoryTable is NULL                                   */
+/*  LVREV_NULLADDRESS       When requesting memory requirements and pInstanceParams     */
+/*                          is NULL                                                     */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1.  This function may be interrupted by the LVREV_Process function                  */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_GetMemoryTable(LVREV_Handle_t           hInstance,
+                                           LVREV_MemoryTable_st     *pMemoryTable,
+                                           LVREV_InstanceParams_st  *pInstanceParams)
+{
+
+    INST_ALLOC              SlowData;
+    INST_ALLOC              FastData;
+    INST_ALLOC              FastCoef;
+    INST_ALLOC              Temporary;
+    LVM_INT16               i;
+    LVM_UINT16              MaxBlockSize;
+
+
+    /*
+     * Check for error conditions
+     */
+    /* Check for NULL pointer */
+    if (pMemoryTable == LVM_NULL)
+    {
+        return(LVREV_NULLADDRESS);
+    }
+
+    /*
+     * Check all instance parameters are in range
+     */
+    if (pInstanceParams != LVM_NULL)
+    {
+        /*
+         * Call for memory allocation, so check the parameters
+         */
+        /* Check for a non-zero block size */
+        if (pInstanceParams->MaxBlockSize == 0)
+        {
+            return LVREV_OUTOFRANGE;
+        }
+
+        /* Check for a valid number of delay lines */
+        if ((pInstanceParams->NumDelays != LVREV_DELAYLINES_1) &&
+            (pInstanceParams->NumDelays != LVREV_DELAYLINES_2) &&
+            (pInstanceParams->NumDelays != LVREV_DELAYLINES_4))
+        {
+            return LVREV_OUTOFRANGE;
+        }
+    }
+
+    /*
+     * Initialise the InstAlloc instances
+     */
+    InstAlloc_Init(&SlowData,  (void *)LVM_NULL);
+    InstAlloc_Init(&FastData,  (void *)LVM_NULL);
+    InstAlloc_Init(&FastCoef,  (void *)LVM_NULL);
+    InstAlloc_Init(&Temporary, (void *)LVM_NULL);
+
+
+    /*
+     * Fill in the memory table
+     */
+    if (hInstance == LVM_NULL)
+    {
+        /*
+         * Check for null pointers
+         */
+        if (pInstanceParams == LVM_NULL)
+        {
+            return(LVREV_NULLADDRESS);
+        }
+
+
+        /*
+         * Select the maximum internal block size
+         */
+        if(pInstanceParams->NumDelays ==LVREV_DELAYLINES_4)
+        {
+            MaxBlockSize = LVREV_MAX_AP3_DELAY;
+        }
+        else if(pInstanceParams->NumDelays ==LVREV_DELAYLINES_2)
+        {
+            MaxBlockSize = LVREV_MAX_AP1_DELAY;
+        }
+        else
+        {
+            MaxBlockSize = LVREV_MAX_AP0_DELAY;
+        }
+
+        if(MaxBlockSize>pInstanceParams->MaxBlockSize)
+        {
+            MaxBlockSize=pInstanceParams->MaxBlockSize;
+        }
+
+
+        /*
+         * Slow data memory
+         */
+        InstAlloc_AddMember(&SlowData, sizeof(LVREV_Instance_st));
+        pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Size         = InstAlloc_GetTotal(&SlowData);
+        pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Type         = LVM_PERSISTENT_SLOW_DATA;
+        pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress = LVM_NULL;
+
+
+        /*
+         * Persistent fast data memory
+         */
+        InstAlloc_AddMember(&FastData, sizeof(LVREV_FastData_st));
+        if(pInstanceParams->NumDelays == LVREV_DELAYLINES_4)
+        {
+            InstAlloc_AddMember(&FastData, LVREV_MAX_T3_DELAY  * sizeof(LVM_INT32));
+            InstAlloc_AddMember(&FastData, LVREV_MAX_T2_DELAY  * sizeof(LVM_INT32));
+            InstAlloc_AddMember(&FastData, LVREV_MAX_T1_DELAY * sizeof(LVM_INT32));
+            InstAlloc_AddMember(&FastData, LVREV_MAX_T0_DELAY * sizeof(LVM_INT32));
+        }
+
+        if(pInstanceParams->NumDelays == LVREV_DELAYLINES_2)
+        {
+            InstAlloc_AddMember(&FastData, LVREV_MAX_T1_DELAY * sizeof(LVM_INT32));
+            InstAlloc_AddMember(&FastData, LVREV_MAX_T0_DELAY * sizeof(LVM_INT32));
+        }
+
+        if(pInstanceParams->NumDelays == LVREV_DELAYLINES_1)
+        {
+            InstAlloc_AddMember(&FastData, LVREV_MAX_T0_DELAY * sizeof(LVM_INT32));
+        }
+
+        pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Size         = InstAlloc_GetTotal(&FastData);
+        pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Type         = LVM_PERSISTENT_FAST_DATA;
+        pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress = LVM_NULL;
+
+
+        /*
+         * Persistent fast coefficient memory
+         */
+        InstAlloc_AddMember(&FastCoef, sizeof(LVREV_FastCoef_st));
+        pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Size         = InstAlloc_GetTotal(&FastCoef);
+        pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Type         = LVM_PERSISTENT_FAST_COEF;
+        pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress = LVM_NULL;
+
+
+        /*
+         * Temporary fast memory
+         */
+        InstAlloc_AddMember(&Temporary, sizeof(LVM_INT32) * MaxBlockSize);          /* General purpose scratch memory */
+        InstAlloc_AddMember(&Temporary, 2*sizeof(LVM_INT32) * MaxBlockSize);        /* Mono->stereo input saved for end mix */
+
+        if(pInstanceParams->NumDelays == LVREV_DELAYLINES_4)
+        {
+            for(i=0; i<4; i++)
+            {
+                InstAlloc_AddMember(&Temporary, sizeof(LVM_INT32) * MaxBlockSize);      /* A Scratch buffer for each delay line */
+            }
+        }
+
+        if(pInstanceParams->NumDelays == LVREV_DELAYLINES_2)
+        {
+            for(i=0; i<2; i++)
+            {
+                InstAlloc_AddMember(&Temporary, sizeof(LVM_INT32) * MaxBlockSize);      /* A Scratch buffer for each delay line */
+            }
+        }
+
+        if(pInstanceParams->NumDelays == LVREV_DELAYLINES_1)
+        {
+            for(i=0; i<1; i++)
+            {
+                InstAlloc_AddMember(&Temporary, sizeof(LVM_INT32) * MaxBlockSize);      /* A Scratch buffer for each delay line */
+            }
+        }
+
+        pMemoryTable->Region[LVM_TEMPORARY_FAST].Size         = InstAlloc_GetTotal(&Temporary);
+        pMemoryTable->Region[LVM_TEMPORARY_FAST].Type         = LVM_TEMPORARY_FAST;
+        pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress = LVM_NULL;
+
+    }
+    else
+    {
+        LVREV_Instance_st   *pLVREV_Private = (LVREV_Instance_st *)hInstance;
+
+
+        /*
+         * Read back memory allocation table
+         */
+        *pMemoryTable = pLVREV_Private->MemoryTable;
+    }
+
+
+    return(LVREV_SUCCESS);
+}
+
+/* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h b/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
new file mode 100755
index 0000000..896b051
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/****************************************************************************************/
+/*                                                                                      */
+/*     Project::                                                                        */
+/*     $Author: nxp007753 $ */
+/*     $Revision: 1316 $ */
+/*     $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $ */
+/*                                                                                      */
+/****************************************************************************************/
+
+#ifndef __LVREV_PRIVATE_H__
+#define __LVREV_PRIVATE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Includes                                                                            */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVREV.h"
+#include "LVREV_Tables.h"
+#include "BIQUAD.h"
+#include "Filter.h"
+#include "VectorArithmetic.h"
+#include "Mixer.h"
+#include "LVM_Macros.h"
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Defines                                                                             */
+/*                                                                                      */
+/****************************************************************************************/
+/* General */
+#define ONE_OVER_SQRT_TWO               23170           /* 1/sqrt(2) * 2^15 */
+#define LVREV_B_8_on_1000            17179869           /* 0.8 * 2^31 */
+#define LVREV_HEADROOM                   8192           /* -12dB * 2^15 */
+#define LVREV_2_9_INQ29           1583769190L           /* 2.9 in Q29 format */
+#define LVREV_MIN3DB                   0x5A82           /* -3dB in Q15 format */
+
+/* Intenal constants */
+#define LVREV_LP_Poly_Order                 4
+#define LVREV_LP_Poly_Shift                 5
+#define LVREV_T_3_Power_0_on_4          32768
+#define LVREV_T_3_Power_1_on_4          43125
+#define LVREV_T_3_Power_2_on_4          56755
+#define LVREV_T_3_Power_3_on_4          74694
+#define LVREV_T60_SCALE                306774           /*(32767/7000)<<16 */
+#define LVREV_T_3_Power_minus0_on_4     32767           /* 3^(-0/4) * 2^15 */
+#define LVREV_T_3_Power_minus1_on_4     24898           /* 3^(-1/4) * 2^15 */
+#define LVREV_T_3_Power_minus2_on_4     18919           /* 3^(-2/4) * 2^15 */
+#define LVREV_T_3_Power_minus3_on_4     14375           /* 3^(-3/4) * 2^15 */
+#define LVREV_MAX_T3_DELAY               2527           /* ((48000 * 120 * LVREV_T_3_Power_minus3_on_4) >> 15) / 1000 */
+#define LVREV_MAX_T2_DELAY               3326           /* ((48000 * 120 * LVREV_T_3_Power_minus2_on_4) >> 15) / 1000 */
+#define LVREV_MAX_T1_DELAY               4377           /* ((48000 * 120 * LVREV_T_3_Power_minus1_on_4) >> 15) / 1000 */
+#define LVREV_MAX_T0_DELAY               5760           /* ((48000 * 120 * LVREV_T_3_Power_minus0_on_4) >> 15) / 1000 */
+#define LVREV_MAX_AP3_DELAY              1685           /* ((48000 * 120 * LVREV_T_3_Power_minus3_on_4) >> 15) / 1500 */
+#define LVREV_MAX_AP2_DELAY              2218           /* ((48000 * 120 * LVREV_T_3_Power_minus2_on_4) >> 15) / 1500 */
+#define LVREV_MAX_AP1_DELAY              2918           /* ((48000 * 120 * LVREV_T_3_Power_minus1_on_4) >> 15) / 1500 */
+#define LVREV_MAX_AP0_DELAY              3840           /* ((48000 * 120 * LVREV_T_3_Power_minus0_on_4) >> 15) / 1500 */
+#define LVREV_BYPASSMIXER_TC             1000           /* Bypass mixer time constant*/
+#define LVREV_ALLPASS_TC                 1000           /* All-pass filter time constant */
+#define LVREV_ALLPASS_TAP_TC             10000           /* All-pass filter dely tap change */
+#define LVREV_FEEDBACKMIXER_TC            100           /* Feedback mixer time constant*/
+#define LVREV_OUTPUTGAIN_SHIFT              5           /* Bits shift for output gain correction */
+
+/* Parameter limits */
+#define LVREV_NUM_FS                        9           /* Number of supported sample rates */
+#define LVREV_MAXBLKSIZE_LIMIT             64           /* Maximum block size low limit */
+#define LVREV_MAX_LEVEL                   100           /* Maximum level, 100% */
+#define LVREV_MIN_LPF_CORNER               50           /* Low pass filter limits */
+#define LVREV_MAX_LPF_CORNER            23999
+#define LVREV_MIN_HPF_CORNER               20           /* High pass filrer limits */
+#define LVREV_MAX_HPF_CORNER             1000
+#define LVREV_MAX_T60                    7000           /* Maximum T60 time in ms */
+#define LVREV_MAX_DENSITY                 100           /* Maximum density, 100% */
+#define LVREV_MAX_DAMPING                 100           /* Maximum damping, 100% */
+#define LVREV_MAX_ROOMSIZE                100           /* Maximum room size, 100% */
+
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Structures                                                                          */
+/*                                                                                      */
+/****************************************************************************************/
+/* Fast data structure */
+typedef struct
+{
+
+    Biquad_1I_Order1_Taps_t HPTaps;                     /* High pass filter taps */
+    Biquad_1I_Order1_Taps_t LPTaps;                     /* Low pass filter taps */
+    Biquad_1I_Order1_Taps_t RevLPTaps[4];               /* Reverb low pass filters taps */
+
+} LVREV_FastData_st;
+
+
+/* Fast coefficient structure */
+typedef struct
+{
+
+    Biquad_Instance_t       HPCoefs;                    /* High pass filter coefficients */
+    Biquad_Instance_t       LPCoefs;                    /* Low pass filter coefficients */
+    Biquad_Instance_t       RevLPCoefs[4];              /* Reverb low pass filters coefficients */
+
+} LVREV_FastCoef_st;
+
+
+/* Instance parameter structure */
+typedef struct
+{
+    /* General */
+    LVREV_InstanceParams_st InstanceParams;             /* Initialisation time instance parameters */
+    LVREV_MemoryTable_st    MemoryTable;                /* Memory table */
+    LVREV_ControlParams_st  CurrentParams;              /* Parameters being used */
+    LVREV_ControlParams_st  NewParams;                  /* New parameters from the calling application */
+    LVM_CHAR                bControlPending;            /* Flag to indicate new parameters are available */
+    LVM_CHAR                bFirstControl;              /* Flag to indicate that the control function is called for the first time */
+    LVM_CHAR                bDisableReverb;             /* Flag to indicate that the mix level is 0% and the reverb can be disabled */
+    LVM_INT32               RoomSizeInms;               /* Room size in msec */
+    LVM_INT32               MaxBlkLen;                  /* Maximum block size for internal processing */
+
+    /* Aligned memory pointers */
+    LVREV_FastData_st       *pFastData;                 /* Fast data memory base address */
+    LVREV_FastCoef_st       *pFastCoef;                 /* Fast coefficient memory base address */
+    LVM_INT32               *pScratchDelayLine[4];      /* Delay line scratch memory */
+    LVM_INT32               *pScratch;                  /* Multi ussge scratch */
+    LVM_INT32               *pInputSave;                /* Reverb block input save for dry/wet mixing*/
+
+    /* Feedback matrix */
+    Mix_1St_Cll_t           FeedbackMixer[4];           /* Mixer for Pop and Click Supression caused by feedback Gain */
+
+    /* All-Pass Filter */
+    LVM_INT32               T[4];                       /* Maximum delay size of buffer */
+    LVM_INT32               *pDelay_T[4];               /* Pointer to delay buffers */
+    LVM_INT32               Delay_AP[4];                /* Offset to AP delay buffer start */
+    LVM_INT16               AB_Selection;               /* Smooth from tap A to B when 1 otherwise B to A */
+    LVM_INT32               A_DelaySize[4];             /* A delay length in samples */
+    LVM_INT32               B_DelaySize[4];             /* B delay length in samples */
+    LVM_INT32               *pOffsetA[4];               /* Offset for the A delay tap */
+    LVM_INT32               *pOffsetB[4];               /* Offset for the B delay tap */
+    Mix_2St_Cll_t           Mixer_APTaps[4];            /* Smoothed AP delay mixer */
+    Mix_1St_Cll_t           Mixer_SGFeedback[4];        /* Smoothed SAfeedback gain */
+    Mix_1St_Cll_t           Mixer_SGFeedforward[4];     /* Smoothed AP feedforward gain */
+
+    /* Output gain */
+    Mix_2St_Cll_t           BypassMixer;                /* Dry/wet mixer */
+    LVM_INT16               Gain;                       /* Gain applied to output to maintain average signal power */
+    Mix_1St_Cll_t           GainMixer;                  /* Gain smoothing */
+
+} LVREV_Instance_st;
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Function prototypes                                                                 */
+/*                                                                                      */
+/****************************************************************************************/
+
+LVREV_ReturnStatus_en   LVREV_ApplyNewSettings(LVREV_Instance_st     *pPrivate);
+
+void                    ReverbBlock(LVM_INT32           *pInput,
+                                    LVM_INT32           *pOutput,
+                                    LVREV_Instance_st   *pPrivate,
+                                    LVM_UINT16          NumSamples);
+
+LVM_INT32               BypassMixer_Callback(void       *pCallbackData,
+                                             void       *pGeneralPurpose,
+                                             LVM_INT16  GeneralPurpose );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /** __LVREV_PRIVATE_H__ **/
+
+/* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Process.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_Process.c
new file mode 100755
index 0000000..822ac8f
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Process.c
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/****************************************************************************************/
+/*                                                                                      */
+/*     Project::                                                                        */
+/*     $Author: nxp007753 $ */
+/*     $Revision: 1316 $ */
+/*     $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $ */
+/*                                                                                      */
+/****************************************************************************************/
+
+/****************************************************************************************/
+/*                                                                                      */
+/* Includes                                                                             */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVREV_Private.h"
+#include "VectorArithmetic.h"
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_Process                                               */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  Process function for the LVREV module.                                              */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance handle                                             */
+/*  pInData                 Pointer to the input data                                   */
+/*  pOutData                Pointer to the output data                                  */
+/*  NumSamples              Number of samples in the input buffer                       */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_Success           Succeeded                                                   */
+/*  LVREV_INVALIDNUMSAMPLES NumSamples was larger than the maximum block size           */
+/*  LVREV_NULLADDRESS       When one of hInstance, pInData or pOutData is NULL          */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1. The input and output buffers must be 32-bit aligned                              */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_Process(LVREV_Handle_t      hInstance,
+                                    const LVM_INT32     *pInData,
+                                    LVM_INT32           *pOutData,
+                                    const LVM_UINT16    NumSamples)
+{
+   LVREV_Instance_st     *pLVREV_Private = (LVREV_Instance_st *)hInstance;
+   LVM_INT32             *pInput  = (LVM_INT32 *)pInData;
+   LVM_INT32             *pOutput = pOutData;
+   LVM_INT32             SamplesToProcess, RemainingSamples, format;
+
+    /*
+     * Check for error conditions
+     */
+
+    /* Check for NULL pointers */
+    if((hInstance == LVM_NULL) || (pInData == LVM_NULL) || (pOutData == LVM_NULL))
+    {
+        return LVREV_NULLADDRESS;
+    }
+
+
+    /*
+     * Apply the new controls settings if required
+     */
+    if(pLVREV_Private->bControlPending == LVM_TRUE)
+    {
+        LVREV_ReturnStatus_en   errorCode;
+
+        /*
+         * Clear the pending flag and update the control settings
+         */
+        pLVREV_Private->bControlPending = LVM_FALSE;
+
+        errorCode = LVREV_ApplyNewSettings (pLVREV_Private);
+
+        if(errorCode != LVREV_SUCCESS)
+        {
+            return errorCode;
+        }
+    }
+
+    /*
+     * Trap the case where the number of samples is zero.
+     */
+    if (NumSamples == 0)
+    {
+        return LVREV_SUCCESS;
+    }
+
+    RemainingSamples = (LVM_INT32)NumSamples;
+
+    format = 1;
+    if (pLVREV_Private->CurrentParams.SourceFormat != LVM_MONO)
+    {
+        format = 2;
+    }
+
+    while (RemainingSamples!=0)
+    {
+        /*
+         * If OFF copy and reformat the data as necessary
+         */
+        if (pLVREV_Private->CurrentParams.OperatingMode == LVM_MODE_OFF)
+        {
+            if((pInput != pOutput) || (pLVREV_Private->CurrentParams.SourceFormat == LVM_MONO))
+            {
+                /*
+                 * Copy the data to the output buffer
+                 */
+
+                if (pLVREV_Private->CurrentParams.SourceFormat != LVM_MONO)
+                {
+                    RemainingSamples = (RemainingSamples << 1);           /* Stereo data */
+                }
+
+                Copy_16((LVM_INT16 *)pInput,
+                        (LVM_INT16 *)pOutput,
+                        (LVM_INT16)(RemainingSamples << 1));
+            }
+
+            RemainingSamples = 0;
+        }
+
+        /*
+         * Process the data
+         */
+        else
+        {
+
+            if(RemainingSamples >  pLVREV_Private->MaxBlkLen)
+            {
+                SamplesToProcess =  pLVREV_Private->MaxBlkLen;
+                RemainingSamples = (LVM_INT16)(RemainingSamples - SamplesToProcess);
+            }
+            else
+            {
+                SamplesToProcess = RemainingSamples;
+                RemainingSamples = 0;
+            }
+
+            ReverbBlock(pInput, pOutput, pLVREV_Private, (LVM_UINT16)SamplesToProcess);
+
+            pInput  = (LVM_INT32 *)(pInput +(SamplesToProcess*format));
+            pOutput = (LVM_INT32 *)(pOutput+(SamplesToProcess*format));
+        }
+    }
+
+    return LVREV_SUCCESS;
+}
+
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                ReverbBlock                                                 */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  Process function for the LVREV module.                                              */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance handle                                             */
+/*  pInData                 Pointer to the input data                                   */
+/*  pOutData                Pointer to the output data                                  */
+/*  NumSamples              Number of samples in the input buffer                       */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVREV_Success           Succeeded                                                   */
+/*  LVREV_INVALIDNUMSAMPLES NumSamples was larger than the maximum block size           */
+/*  LVREV_NULLADDRESS       When one of hInstance, pInData or pOutData is NULL          */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1. The input and output buffers must be 32-bit aligned                              */
+/*                                                                                      */
+/****************************************************************************************/
+
+void ReverbBlock(LVM_INT32 *pInput, LVM_INT32 *pOutput, LVREV_Instance_st *pPrivate, LVM_UINT16 NumSamples)
+{
+    LVM_INT16   j, size;
+    LVM_INT32   *pDelayLine;
+    LVM_INT32   *pDelayLineInput = pPrivate->pScratch;
+    LVM_INT32   *pScratch = pPrivate->pScratch;
+    LVM_INT32   *pIn;
+    LVM_INT32   *pTemp = pPrivate->pInputSave;
+    LVM_INT32   NumberOfDelayLines;
+
+    /******************************************************************************
+     * All calculations will go into the buffer pointed to by pTemp, this will    *
+     * then be mixed with the original input to create the final output.          *
+     *                                                                            *
+     * When INPLACE processing is selected this must be a temporary buffer and    *
+     * hence this is the worst case, so for simplicity this will ALWAYS be so     *
+     *                                                                            *
+     * The input buffer will remain untouched until the output of the mixer if    *
+     * INPLACE processing is selected.                                            *
+     *                                                                            *
+     * The temp buffer will always be NumSamples in size regardless of MONO or    *
+     * STEREO input. In the case of stereo input all processing is done in MONO   *
+     * and the final output is converted to STEREO after the mixer                *
+     ******************************************************************************/
+
+    if(pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_4 )
+    {
+        NumberOfDelayLines = 4;
+    }
+    else if(pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_2 )
+    {
+        NumberOfDelayLines = 2;
+    }
+    else
+    {
+        NumberOfDelayLines = 1;
+    }
+
+    if(pPrivate->CurrentParams.SourceFormat == LVM_MONO)
+    {
+        pIn = pInput;
+    }
+    else
+    {
+        /*
+         *  Stereo to mono conversion
+         */
+
+        From2iToMono_32( pInput,
+                         pTemp,
+                         (LVM_INT16)NumSamples);
+
+        pIn = pTemp;
+    }
+
+    Mult3s_32x16(pIn,
+                 (LVM_INT16)LVREV_HEADROOM,
+                 pTemp,
+                 (LVM_INT16)NumSamples);
+
+    /*
+     *  High pass filter
+     */
+    FO_1I_D32F32C31_TRC_WRA_01( &pPrivate->pFastCoef->HPCoefs,
+                                pTemp,
+                                pTemp,
+                                (LVM_INT16)NumSamples);
+    /*
+     *  Low pass filter
+     */
+    FO_1I_D32F32C31_TRC_WRA_01( &pPrivate->pFastCoef->LPCoefs,
+                                pTemp,
+                                pTemp,
+                                (LVM_INT16)NumSamples);
+
+    /*
+     *  Process all delay lines
+     */
+
+    for(j = 0; j < NumberOfDelayLines; j++)
+    {
+        pDelayLine = pPrivate->pScratchDelayLine[j];
+
+        /*
+         * All-pass filter with pop and click suppression
+         */
+        /* Get the smoothed, delayed output. Put it in the output buffer */
+        MixSoft_2St_D32C31_SAT(&pPrivate->Mixer_APTaps[j],
+                               pPrivate->pOffsetA[j],
+                               pPrivate->pOffsetB[j],
+                               pDelayLine,
+                               (LVM_INT16)NumSamples);
+        /* Re-align the all pass filter delay buffer and copying the fixed delay data to the AP delay in the process */
+        Copy_16((LVM_INT16 *)&pPrivate->pDelay_T[j][NumSamples],
+                (LVM_INT16 *)pPrivate->pDelay_T[j],
+                (LVM_INT16)((pPrivate->T[j]-NumSamples) << 1));         /* 32-bit data */
+        /* Apply the smoothed feedback and save to fixed delay input (currently empty) */
+        MixSoft_1St_D32C31_WRA(&pPrivate->Mixer_SGFeedback[j],
+                               pDelayLine,
+                               &pPrivate->pDelay_T[j][pPrivate->T[j]-NumSamples],
+                               (LVM_INT16)NumSamples);
+        /* Sum into the AP delay line */
+        Mac3s_Sat_32x16(&pPrivate->pDelay_T[j][pPrivate->T[j]-NumSamples],
+                        -0x7fff,                                        /* Invert since the feedback coefficient is negative */
+                        &pPrivate->pDelay_T[j][pPrivate->Delay_AP[j]-NumSamples],
+                        (LVM_INT16)NumSamples);
+        /* Apply smoothed feedforward sand save to fixed delay input (currently empty) */
+        MixSoft_1St_D32C31_WRA(&pPrivate->Mixer_SGFeedforward[j],
+                               &pPrivate->pDelay_T[j][pPrivate->Delay_AP[j]-NumSamples],
+                               &pPrivate->pDelay_T[j][pPrivate->T[j]-NumSamples],
+                               (LVM_INT16)NumSamples);
+        /* Sum into the AP output */
+        Mac3s_Sat_32x16(&pPrivate->pDelay_T[j][pPrivate->T[j]-NumSamples],
+                        0x7fff,
+                        pDelayLine,
+                        (LVM_INT16)NumSamples);
+
+        /*
+         *  Feedback gain
+         */
+        MixSoft_1St_D32C31_WRA(&pPrivate->FeedbackMixer[j], pDelayLine, pDelayLine, NumSamples);
+
+        /*
+         *  Low pass filter
+         */
+        FO_1I_D32F32C31_TRC_WRA_01( &pPrivate->pFastCoef->RevLPCoefs[j],
+                                    pDelayLine,
+                                    pDelayLine,
+                                    (LVM_INT16)NumSamples);
+    }
+
+    /*
+     *  Apply rotation matrix and delay samples
+     */
+    for(j = 0; j < NumberOfDelayLines; j++)
+    {
+
+        Copy_16( (LVM_INT16*)(pTemp),
+                 (LVM_INT16*)(pDelayLineInput),
+                 (LVM_INT16)(NumSamples << 1));
+
+        /*
+         *  Rotation matrix mix
+         */
+        switch(j)
+        {
+            case 3:
+                /*
+                 *  Add delay line 1 and 2 contribution
+                 */
+                 Mac3s_Sat_32x16(pPrivate->pScratchDelayLine[1], -0x8000, pDelayLineInput, (LVM_INT16)NumSamples);
+                 Mac3s_Sat_32x16(pPrivate->pScratchDelayLine[2], -0x8000, pDelayLineInput, (LVM_INT16)NumSamples);
+
+                break;
+            case 2:
+
+                /*
+                 *  Add delay line 0 and 3 contribution
+                 */
+                 Mac3s_Sat_32x16(pPrivate->pScratchDelayLine[0], -0x8000, pDelayLineInput, (LVM_INT16)NumSamples);
+                 Mac3s_Sat_32x16(pPrivate->pScratchDelayLine[3], -0x8000, pDelayLineInput, (LVM_INT16)NumSamples);
+
+                break;
+            case 1:
+                if(pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_4)
+                {
+                    /*
+                     *  Add delay line 0 and 3 contribution
+                     */
+                    Mac3s_Sat_32x16(pPrivate->pScratchDelayLine[0], -0x8000, pDelayLineInput, (LVM_INT16)NumSamples);
+                    Add2_Sat_32x32(pPrivate->pScratchDelayLine[3], pDelayLineInput, (LVM_INT16)NumSamples);
+
+                }
+                else
+                {
+                    /*
+                     *  Add delay line 0 and 1 contribution
+                     */
+                     Mac3s_Sat_32x16(pPrivate->pScratchDelayLine[0], -0x8000, pDelayLineInput, (LVM_INT16)NumSamples);
+                     Mac3s_Sat_32x16(pPrivate->pScratchDelayLine[1], -0x8000, pDelayLineInput, (LVM_INT16)NumSamples);
+
+                }
+                break;
+            case 0:
+                if(pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_4)
+                {
+                    /*
+                     *  Add delay line 1 and 2 contribution
+                     */
+                    Mac3s_Sat_32x16(pPrivate->pScratchDelayLine[1], -0x8000, pDelayLineInput, (LVM_INT16)NumSamples);
+                    Add2_Sat_32x32(pPrivate->pScratchDelayLine[2], pDelayLineInput, (LVM_INT16)NumSamples);
+
+                }
+                else if(pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_2)
+                {
+                    /*
+                     *  Add delay line 0 and 1 contribution
+                     */
+                    Add2_Sat_32x32(pPrivate->pScratchDelayLine[0], pDelayLineInput, (LVM_INT16)NumSamples);
+                    Mac3s_Sat_32x16(pPrivate->pScratchDelayLine[1], -0x8000, pDelayLineInput, (LVM_INT16)NumSamples);
+
+                }
+                else
+                {
+                    /*
+                     *  Add delay line 0 contribution
+                     */
+
+                    /*             SOURCE                          DESTINATION*/
+                    Add2_Sat_32x32(pPrivate->pScratchDelayLine[0], pDelayLineInput, (LVM_INT16)NumSamples);
+                }
+                break;
+            default:
+                break;
+        }
+
+        /*
+         *  Delay samples
+         */
+        Copy_16((LVM_INT16 *)pDelayLineInput,
+                (LVM_INT16 *)&pPrivate->pDelay_T[j][pPrivate->T[j]-NumSamples],
+                (LVM_INT16)(NumSamples << 1));              /* 32-bit data */
+
+    }
+
+
+    /*
+     *  Create stereo output
+     */
+    switch(pPrivate->InstanceParams.NumDelays)
+    {
+        case LVREV_DELAYLINES_4:
+             Add2_Sat_32x32(pPrivate->pScratchDelayLine[3],
+                            pPrivate->pScratchDelayLine[0],
+                            (LVM_INT16)NumSamples);
+             Add2_Sat_32x32(pPrivate->pScratchDelayLine[2],
+                            pPrivate->pScratchDelayLine[1],
+                            (LVM_INT16)NumSamples);
+
+             if(pPrivate->CurrentParams.SourceFormat != LVM_MONO)
+             {
+                JoinTo2i_32x32(pPrivate->pScratchDelayLine[0],
+                               pPrivate->pScratchDelayLine[1],
+                               pTemp,
+                               (LVM_INT16)NumSamples);
+
+             }
+             else
+             {
+                 Add2_Sat_32x32(pPrivate->pScratchDelayLine[1],
+                                pPrivate->pScratchDelayLine[0],
+                                (LVM_INT16)NumSamples);
+
+                /*Apply 3-dB gain in-order to compensate for the gain change in stereo mode*/
+                Mult3s_32x16(pPrivate->pScratchDelayLine[0],
+                             LVREV_MIN3DB,
+                             pTemp,
+                             (LVM_INT16)NumSamples);
+             }
+            break;
+        case LVREV_DELAYLINES_2:
+
+             Copy_16( (LVM_INT16*)pPrivate->pScratchDelayLine[1],
+                      (LVM_INT16*)pScratch,
+                      (LVM_INT16)(NumSamples << 1));
+
+
+
+             if(pPrivate->CurrentParams.SourceFormat != LVM_MONO)
+             {
+
+                Mac3s_Sat_32x16(pPrivate->pScratchDelayLine[0],
+                                -0x8000,
+                                pScratch,
+                                (LVM_INT16)NumSamples);
+             }
+
+             Add2_Sat_32x32(pPrivate->pScratchDelayLine[1],
+                            pPrivate->pScratchDelayLine[0],
+                            (LVM_INT16)NumSamples);
+
+
+             if(pPrivate->CurrentParams.SourceFormat != LVM_MONO)
+             {
+                 JoinTo2i_32x32(pPrivate->pScratchDelayLine[0],
+                                pScratch,
+                                pTemp,
+                                (LVM_INT16)NumSamples);
+             }
+             else
+             {
+                Copy_16(    (LVM_INT16*)pPrivate->pScratchDelayLine[0],
+                            (LVM_INT16*)pTemp,
+                            (LVM_INT16)(NumSamples << 1));
+
+             }
+            break;
+        case LVREV_DELAYLINES_1:
+            if(pPrivate->CurrentParams.SourceFormat != LVM_MONO)
+            {
+
+                MonoTo2I_32(pPrivate->pScratchDelayLine[0],
+                            pTemp,
+                            (LVM_INT16)NumSamples);
+            }
+            else
+            {
+                pTemp = pPrivate->pScratchDelayLine[0];
+            }
+            break;
+        default:
+            break;
+    }
+
+
+    /*
+     *  Dry/wet mixer
+     */
+    if(pPrivate->CurrentParams.SourceFormat != LVM_MONO)
+    {
+        size = (LVM_INT16)(NumSamples << 1);
+    }
+    else
+    {
+        size = (LVM_INT16)NumSamples;
+    }
+
+    MixSoft_2St_D32C31_SAT(&pPrivate->BypassMixer,
+                           pInput,
+                           pTemp,
+                           pOutput,
+                           size);
+
+    /* Apply Gain*/
+    if(pPrivate->CurrentParams.SourceFormat != LVM_MONO)
+    {
+        size = (LVM_INT16)(NumSamples << 1);
+    }
+    else
+    {
+        size = (LVM_INT16)NumSamples;
+    }
+
+    Shift_Sat_v32xv32 (LVREV_OUTPUTGAIN_SHIFT,
+                       pOutput,
+                       pOutput,
+                       size);
+
+    MixSoft_1St_D32C31_WRA(&pPrivate->GainMixer,
+                           pOutput,
+                           pOutput,
+                           size);
+    return;
+}
+
+
+/* End of file */
+
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c
new file mode 100755
index 0000000..124fd3b
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/****************************************************************************************/
+/*                                                                                      */
+/*     Project::                                                                        */
+/*     $Author: nxp007753 $ */
+/*     $Revision: 1316 $ */
+/*     $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $ */
+/*                                                                                      */
+/****************************************************************************************/
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Includes                                                                            */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVREV_Private.h"
+
+/****************************************************************************************/
+/*                                                                                      */
+/* FUNCTION:                LVREV_SetControlParameters                                  */
+/*                                                                                      */
+/* DESCRIPTION:                                                                         */
+/*  Sets or changes the LVREV module parameters.                                        */
+/*                                                                                      */
+/* PARAMETERS:                                                                          */
+/*  hInstance               Instance handle                                             */
+/*  pNewParams              Pointer to a parameter structure                            */
+/*                                                                                      */
+/* RETURNS:                                                                             */
+/*  LVM_Success             Succeeded                                                   */
+/*  LVREV_NULLADDRESS       When hInstance or pNewParams is NULL                        */
+/*  LVREV_OUTOFRANGE        When any of the new parameters is out of range              */
+/*                                                                                      */
+/* NOTES:                                                                               */
+/*  1.  This function may be interrupted by the LVREV_Process function                  */
+/*                                                                                      */
+/****************************************************************************************/
+LVREV_ReturnStatus_en LVREV_SetControlParameters(LVREV_Handle_t           hInstance,
+                                                 LVREV_ControlParams_st   *pNewParams)
+{
+
+    LVREV_Instance_st     *pLVREV_Private = (LVREV_Instance_st *)hInstance;
+
+
+    /*
+     * Check for error conditions
+     */
+    if((hInstance == LVM_NULL) || (pNewParams == LVM_NULL))
+    {
+        return LVREV_NULLADDRESS;
+    }
+
+    /*
+     * Check all new control parameters are in range
+     */
+    if(    ((pNewParams->OperatingMode != LVM_MODE_OFF) && (pNewParams->OperatingMode != LVM_MODE_ON))                                         ||
+        ((pNewParams->SampleRate != LVM_FS_8000) && (pNewParams->SampleRate != LVM_FS_11025) && (pNewParams->SampleRate != LVM_FS_12000)       &&
+        (pNewParams->SampleRate != LVM_FS_16000) && (pNewParams->SampleRate != LVM_FS_22050) && (pNewParams->SampleRate != LVM_FS_24000)       &&
+        (pNewParams->SampleRate != LVM_FS_32000) && (pNewParams->SampleRate != LVM_FS_44100) && (pNewParams->SampleRate != LVM_FS_48000))      ||
+        ((pNewParams->SourceFormat != LVM_STEREO) && (pNewParams->SourceFormat != LVM_MONOINSTEREO) && (pNewParams->SourceFormat != LVM_MONO)) )
+    {
+        return (LVREV_OUTOFRANGE);
+    }
+
+
+    if (pNewParams->Level > LVREV_MAX_LEVEL)
+    {
+        return LVREV_OUTOFRANGE;
+    }
+
+    if ((pNewParams->LPF < LVREV_MIN_LPF_CORNER) || (pNewParams->LPF > LVREV_MAX_LPF_CORNER))
+    {
+        return LVREV_OUTOFRANGE;
+    }
+
+    if ((pNewParams->HPF < LVREV_MIN_HPF_CORNER) || (pNewParams->HPF > LVREV_MAX_HPF_CORNER))
+    {
+        return LVREV_OUTOFRANGE;
+    }
+
+    if (pNewParams->T60 > LVREV_MAX_T60)
+    {
+        return LVREV_OUTOFRANGE;
+    }
+
+    if (pNewParams->Density > LVREV_MAX_DENSITY)
+    {
+        return LVREV_OUTOFRANGE;
+    }
+
+    if (pNewParams->Damping > LVREV_MAX_DAMPING)
+    {
+        return LVREV_OUTOFRANGE;
+    }
+
+    if (pNewParams->RoomSize > LVREV_MAX_ROOMSIZE)
+    {
+        return LVREV_OUTOFRANGE;
+    }
+
+
+
+    /*
+     * Copy the new parameters and set the flag to indicate they are available
+     */
+    pLVREV_Private->NewParams       = *pNewParams;
+    pLVREV_Private->bControlPending = LVM_TRUE;
+
+    return LVREV_SUCCESS;
+}
+
+/* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.c
new file mode 100755
index 0000000..9249a31
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/****************************************************************************************/
+/*                                                                                      */
+/*     Project::                                                                        */
+/*     $Author: beq07716 $ */
+/*     $Revision: 1011 $ */
+/*     $Date: 2010-06-28 15:07:08 +0200 (Mon, 28 Jun 2010) $ */
+/*                                                                                      */
+/****************************************************************************************/
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Includes                                                                            */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVREV.h"
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Tables                                                                              */
+/*                                                                                      */
+/****************************************************************************************/
+
+/* Table with supported sampling rates.  The table can be indexed using LVM_Fs_en       */
+const LVM_UINT16 LVM_FsTable[] = {
+    8000 ,
+    11025,
+    12000,
+    16000,
+    22050,
+    24000,
+    32000,
+    44100,
+    48000
+};
+
+/* Table with supported sampling rates.  The table can be indexed using LVM_Fs_en       */
+LVM_UINT16 LVM_GetFsFromTable(LVM_Fs_en FsIndex){
+    if (FsIndex > LVM_FS_48000)
+        return 0;
+
+    return (LVM_FsTable[FsIndex]);
+}
+
+/* In order to maintain consistant input and out put signal strengths
+   output gain/attenuation is applied. This gain depends on T60 and Rooms
+   size parameters. These polynomial coefficients are calculated experimentally.
+   First value in the table is room size
+   second value is  A0
+   third value is   A1
+   fourth value is  A2
+   fifth value is   A3
+   sixth value is   A4
+
+     shift value  is to be added array (to use LVM_Polynomial function)
+
+  The gain is calculated using variable x=(T60*32767/7000)*32768;
+
+   first values is used to get polynomial set for given room size,
+   For room sizes which are not in the table, linear interpolation can be used.
+
+  */
+
+/* Normalizing output including Reverb Level part (only shift up)*/
+const LVM_INT32 LVREV_GainPolyTable[24][5]={{1,17547434,128867434,-120988896,50761228,},
+                                            {2,18256869,172666902,-193169292,88345744,},
+                                            {3,16591311,139250151,-149667234,66770059,},
+                                            {4,17379977,170835131,-173579321,76278163,},
+                                            {5,18963512,210364934,-228623519,103435022,},
+                                            {6,17796318,135756417,-144084053,64327698,},
+                                            {7,17454695,174593214,-187513064,85146582,},
+                                            {8,17229257,140715570,-145790588,65361740,},
+                                            {9,17000547,163195946,-176733969,79562130,},
+                                            {10,16711699,142476304,-133339887,58366547,},
+                                            {13,18108419,149223697,-161762020,74397589,},
+                                            {15,16682043,124844884,-134284487,60082180,},
+                                            {17,16627346,120936430,-121766674,53146421,},
+                                            {20,17338325,125432694,-126616983,56534237,},
+                                            {25,16489146,99218217,-94597467,40616506,},
+                                            {30,15582373,84479043,-75365006,30952348,},
+                                            {40,16000669,84896611,-75031127,30696306,},
+                                            {50,15087054,71695031,-59349268,23279669,},
+                                            {60,15830714,68672971,-58211201,23671158,},
+                                            {70,15536061,66657972,-55901437,22560153,},
+                                            {75,15013145,48179917,-24138354,5232074,},
+                                            {80,15688738,50195036,-34206760,11515792,},
+                                            {90,16003322,48323661,-35607378,13153872,},
+                                            {100,15955223,48558201,-33706865,11715792,},
+                                            };
+
+/* End of file */
+
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.h b/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.h
new file mode 100755
index 0000000..98471be
--- /dev/null
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2004-2010 NXP Software
+ * 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.
+ */
+
+/****************************************************************************************/
+/*                                                                                      */
+/*     Project::                                                                        */
+/*     $Author: beq07716 $ */
+/*     $Revision: 773 $ */
+/*     $Date: 2010-06-14 10:43:54 +0200 (Mon, 14 Jun 2010) $ */
+/*                                                                                      */
+/****************************************************************************************/
+
+
+#ifndef _LVREV_TABLES_H_
+#define _LVREV_TABLES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Includes                                                                            */
+/*                                                                                      */
+/****************************************************************************************/
+#include "LVREV_Private.h"
+
+/****************************************************************************************/
+/*                                                                                      */
+/*  Definitions                                                                         */
+/*                                                                                      */
+/****************************************************************************************/
+
+extern const    LVM_UINT16  LVM_FsTable[];
+extern          LVM_UINT16  LVM_GetFsFromTable(LVM_Fs_en FsIndex);
+extern          LVM_INT32   LVREV_GainPolyTable[24][5];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /** _LVREV_TABLES_H_ **/
+
+/* End of file */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c
index fea44bf..668b151 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c
@@ -17,9 +17,9 @@
 
 /************************************************************************************
 
-     $Author: beq06068 $
-     $Revision: 1307 $
-     $Date: 2010-07-22 17:41:25 +0200 (Thu, 22 Jul 2010) $
+     $Author: nxp007753 $
+     $Revision: 1331 $
+     $Date: 2010-07-27 12:26:23 +0200 (Tue, 27 Jul 2010) $
 
 *************************************************************************************/
 
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c
index 7ab6571..d8023d64 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c
@@ -18,8 +18,8 @@
 /************************************************************************************
 
      $Author: nxp007753 $
-     $Revision: 1315 $
-     $Date: 2010-07-23 11:52:08 +0200 (Fri, 23 Jul 2010) $
+     $Revision: 1331 $
+     $Date: 2010-07-27 12:26:23 +0200 (Tue, 27 Jul 2010) $
 
 *************************************************************************************/
 
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c
index f5f7cd0..82a8db2 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c
@@ -17,9 +17,9 @@
 
 /************************************************************************************
 
-     $Author: beq06068 $
-     $Revision: 1307 $
-     $Date: 2010-07-22 17:41:25 +0200 (Thu, 22 Jul 2010) $
+     $Author: nxp007753 $
+     $Revision: 1331 $
+     $Date: 2010-07-27 12:26:23 +0200 (Tue, 27 Jul 2010) $
 
 *************************************************************************************/
 
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c
index b67d824..83748e6 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c
@@ -18,8 +18,8 @@
 /************************************************************************************
 
      $Author: nxp007753 $
-     $Revision: 1315 $
-     $Date: 2010-07-23 11:52:08 +0200 (Fri, 23 Jul 2010) $
+     $Revision: 1331 $
+     $Date: 2010-07-27 12:26:23 +0200 (Tue, 27 Jul 2010) $
 
 *************************************************************************************/
 
diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk
index 7855dcd..2e9b9b4 100644
--- a/media/libeffects/lvm/wrapper/Android.mk
+++ b/media/libeffects/lvm/wrapper/Android.mk
@@ -34,3 +34,36 @@
 
 
 include $(BUILD_SHARED_LIBRARY)
+
+# reverb wrapper
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_SRC_FILES:= \
+    Reverb/EffectReverb.cpp
+
+LOCAL_MODULE:= libreverbwrapper
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_STATIC_LIBRARIES += libreverb
+
+LOCAL_SHARED_LIBRARIES := \
+     libcutils \
+
+ifeq ($(TARGET_SIMULATOR),true)
+LOCAL_LDLIBS += -ldl
+else
+LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_C_INCLUDES += \
+    $(LOCAL_PATH)/Reverb \
+    $(LOCAL_PATH)/../lib/Common/lib/ \
+    $(LOCAL_PATH)/../lib/Reverb/lib/ \
+
+
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index a70bdff..d856eb4 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -17,7 +17,7 @@
 
 #define LOG_TAG "Bundle"
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 
 #include <cutils/log.h>
 #include <assert.h>
@@ -66,7 +66,9 @@
 
 // Flag to allow a one time init of global memory, only happens on first call ever
 int LvmInitFlag = LVM_FALSE;
-SessionContext GlobalSessionMemory[32];
+int LvmSessionsActive = 0;
+SessionContext GlobalSessionMemory[LVM_MAX_SESSIONS];
+int SessionIndex[LVM_MAX_SESSIONS];
 
 // NXP SW BassBoost UUID
 const effect_descriptor_t gBassBoostDescriptor = {
@@ -125,24 +127,24 @@
 int  LvmEffect_disable         (EffectContext *pContext);
 void LvmEffect_free            (EffectContext *pContext);
 int  Effect_configure          (EffectContext *pContext, effect_config_t *pConfig);
-int  BassBoost_setParameter    (EffectContext *pContext, int32_t *pParam, void *pValue);
+int  BassBoost_setParameter    (EffectContext *pContext, void *pParam, void *pValue);
 int  BassBoost_getParameter    (EffectContext *pContext,
-                               int32_t        *pParam,
+                               void           *pParam,
                                size_t         *pValueSize,
                                void           *pValue);
-int  Virtualizer_setParameter  (EffectContext *pContext, int32_t *pParam, void *pValue);
+int  Virtualizer_setParameter  (EffectContext *pContext, void *pParam, void *pValue);
 int  Virtualizer_getParameter  (EffectContext *pContext,
-                               int32_t        *pParam,
+                               void           *pParam,
                                size_t         *pValueSize,
                                void           *pValue);
-int  Equalizer_setParameter    (EffectContext *pContext, int32_t *pParam, void *pValue);
+int  Equalizer_setParameter    (EffectContext *pContext, void *pParam, void *pValue);
 int  Equalizer_getParameter    (EffectContext *pContext,
-                                int32_t       *pParam,
+                                void          *pParam,
                                 size_t        *pValueSize,
                                 void          *pValue);
-int  Volume_setParameter       (EffectContext *pContext, int32_t *pParam, void *pValue);
+int  Volume_setParameter       (EffectContext *pContext, void *pParam, void *pValue);
 int  Volume_getParameter       (EffectContext *pContext,
-                                int32_t       *pParam,
+                                void          *pParam,
                                 size_t        *pValueSize,
                                 void          *pValue);
 
@@ -189,6 +191,7 @@
                             int32_t             ioId,
                             effect_interface_t  *pInterface){
     int ret;
+    int sessionNo;
     int i;
     EffectContext *pContext = new EffectContext;
 
@@ -199,7 +202,7 @@
         return -EINVAL;
     }
 
-    if((sessionId < 0)||(sessionId >= LVM_MAX_SESSIONS)){
+    if(sessionId < 0){
         LOGV("\tLVM_ERROR : EffectCreate sessionId is less than 0");
         return -EINVAL;
     }
@@ -210,16 +213,41 @@
         LvmGlobalBundle_init();
     }
 
+    LOGV("\tEffectCreate: There are %d LVM sessions acive\n", LvmSessionsActive);
+
+    // Find next available sessionNo
+    for(i=0; i<LVM_MAX_SESSIONS; i++){
+        if((SessionIndex[i] == -1)||(SessionIndex[i] == sessionId)){
+            sessionNo       = i;
+            SessionIndex[i] = sessionId;
+            LOGV("\tEffectCreate: Allocating SessionNo %d for SessionId %d\n", sessionNo,sessionId);
+            break;
+        }
+    }
+
+    if(i==LVM_MAX_SESSIONS){
+        LOGV("\tLVM_ERROR : Cannot find memory to allocate for current session");
+        return -EINVAL;
+    }
     // If this is the first create in this session
-    if(GlobalSessionMemory[sessionId].bBundledEffectsEnabled == LVM_FALSE){
-        LOGV("\tEffectCreate - This is the first effect in current session %d", sessionId);
-        LOGV("\tEffectCreate - Setting up Bundled Effects Instance for session %d", sessionId);
+    if(GlobalSessionMemory[sessionNo].bBundledEffectsEnabled == LVM_FALSE){
+        LOGV("\tEffectCreate - This is the first effect in current sessionId %d sessionNo %d",
+                sessionId, sessionNo);
 
-        GlobalSessionMemory[sessionId].bBundledEffectsEnabled = LVM_TRUE;
-        GlobalSessionMemory[sessionId].pBundledContext        = new BundledEffectContext;
+        LvmSessionsActive++;
 
-        pContext->pBundledContext = GlobalSessionMemory[sessionId].pBundledContext;
-        pContext->pBundledContext->SessionNo                = sessionId;
+        if(LvmSessionsActive >= LVM_MAX_SESSIONS){
+            LOGV("\tLVM_ERROR : Number of active session is greater than LVM_MAX_SESSIONS (%d)",
+                  LVM_MAX_SESSIONS);
+            return -EINVAL;
+        }
+
+        GlobalSessionMemory[sessionNo].bBundledEffectsEnabled = LVM_TRUE;
+        GlobalSessionMemory[sessionNo].pBundledContext        = new BundledEffectContext;
+
+        pContext->pBundledContext = GlobalSessionMemory[sessionNo].pBundledContext;
+        pContext->pBundledContext->SessionNo                = sessionNo;
+        pContext->pBundledContext->SessionId                = sessionId;
         pContext->pBundledContext->hInstance                = NULL;
         pContext->pBundledContext->bVolumeEnabled           = LVM_FALSE;
         pContext->pBundledContext->bEqualizerEnabled        = LVM_FALSE;
@@ -267,37 +295,39 @@
         }
     }
     else{
-        pContext->pBundledContext = GlobalSessionMemory[sessionId].pBundledContext;
+        LOGV("\tEffectCreate - Assigning memory for previously created effect on sessionNo %d",
+                sessionNo);
+        pContext->pBundledContext =
+                GlobalSessionMemory[sessionNo].pBundledContext;
     }
-
     LOGV("\tEffectCreate - pBundledContext is %p", pContext->pBundledContext);
 
     // Create each Effect
     if (memcmp(uuid, &gBassBoostDescriptor.uuid, sizeof(effect_uuid_t)) == 0){
         // Create Bass Boost
         LOGV("\tEffectCreate - Effect to be created is LVM_BASS_BOOST");
-        GlobalSessionMemory[sessionId].bBassInstantiated = LVM_TRUE;
+        GlobalSessionMemory[pContext->pBundledContext->SessionNo].bBassInstantiated = LVM_TRUE;
 
         pContext->itfe       = &gLvmEffectInterface;
         pContext->EffectType = LVM_BASS_BOOST;
     } else if (memcmp(uuid, &gVirtualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0){
         // Create Virtualizer
         LOGV("\tEffectCreate - Effect to be created is LVM_VIRTUALIZER");
-        GlobalSessionMemory[sessionId].bVirtualizerInstantiated = LVM_TRUE;
+        GlobalSessionMemory[pContext->pBundledContext->SessionNo].bVirtualizerInstantiated=LVM_TRUE;
 
         pContext->itfe       = &gLvmEffectInterface;
         pContext->EffectType = LVM_VIRTUALIZER;
     } else if (memcmp(uuid, &gEqualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0){
         // Create Equalizer
         LOGV("\tEffectCreate - Effect to be created is LVM_EQUALIZER");
-        GlobalSessionMemory[sessionId].bEqualizerInstantiated = LVM_TRUE;
+        GlobalSessionMemory[pContext->pBundledContext->SessionNo].bEqualizerInstantiated = LVM_TRUE;
 
         pContext->itfe       = &gLvmEffectInterface;
         pContext->EffectType = LVM_EQUALIZER;
     } else if (memcmp(uuid, &gVolumeDescriptor.uuid, sizeof(effect_uuid_t)) == 0){
         // Create Volume
         LOGV("\tEffectCreate - Effect to be created is LVM_VOLUME");
-        GlobalSessionMemory[sessionId].bVolumeInstantiated = LVM_TRUE;
+        GlobalSessionMemory[pContext->pBundledContext->SessionNo].bVolumeInstantiated = LVM_TRUE;
 
         pContext->itfe       = &gLvmEffectInterface;
         pContext->EffectType = LVM_VOLUME;
@@ -316,6 +346,7 @@
     LOGV("\n\tEffectRelease start %p", interface);
     EffectContext * pContext = (EffectContext *)interface;
 
+    LOGV("\n\tEffectRelease start interface: %p, context %p", interface, pContext->pBundledContext);
     if (pContext == NULL){
         LOGV("\tLVM_ERROR : EffectRelease called with NULL pointer");
         return -EINVAL;
@@ -349,13 +380,28 @@
         fclose(pContext->pBundledContext->PcmInPtr);
         fclose(pContext->pBundledContext->PcmOutPtr);
         #endif
+
+        LvmSessionsActive--;
+        LOGV("\tEffectRelease: There are %d LVM sessions remaining\n", LvmSessionsActive);
+
+        // Clear the SessionIndex
+        for(int i=0; i<LVM_MAX_SESSIONS; i++){
+            if(SessionIndex[i] == pContext->pBundledContext->SessionId){
+                SessionIndex[i] = -1;
+                LOGV("\tEffectRelease: Clearing SessionIndex SessionNo %d for SessionId %d\n",
+                        i, pContext->pBundledContext->SessionId);
+                break;
+            }
+        }
+
         LOGV("\tEffectRelease: All effects are no longer instantiated\n");
         GlobalSessionMemory[pContext->pBundledContext->SessionNo].bBundledEffectsEnabled =LVM_FALSE;
         GlobalSessionMemory[pContext->pBundledContext->SessionNo].pBundledContext = LVM_NULL;
         LOGV("\tEffectRelease: Freeing LVM Bundle memory\n");
         LvmEffect_free(pContext);
-        LOGV("\tEffectRelease: Deleting LVM Bundle context\n");
+        LOGV("\tEffectRelease: Deleting LVM Bundle context %p\n", pContext->pBundledContext);
         delete pContext->pBundledContext;
+        pContext->pBundledContext = LVM_NULL;
     }
     // free the effect context for current effect
     delete pContext;
@@ -374,6 +420,8 @@
         GlobalSessionMemory[i].bBassInstantiated        = LVM_FALSE;
         GlobalSessionMemory[i].bVirtualizerInstantiated = LVM_FALSE;
         GlobalSessionMemory[i].pBundledContext          = LVM_NULL;
+
+        SessionIndex[i] = -1;
     }
     return;
 }
@@ -505,6 +553,9 @@
     params.SpeakerType            = LVM_HEADPHONES;
 
     pContext->pBundledContext->SampleRate = LVM_FS_44100;
+    pContext->pBundledContext->SamplesToExitCountEq   = 44100*2*2; // 2 secs Stereo
+    pContext->pBundledContext->SamplesToExitCountBb   = 44100*2*2; // 2 secs Stereo
+    pContext->pBundledContext->SamplesToExitCountVirt = 44100*2*2; // 2 secs Stereo
 
     /* Concert Sound parameters */
     params.VirtualizerOperatingMode   = LVM_MODE_OFF;
@@ -842,21 +893,27 @@
     switch (pConfig->inputCfg.samplingRate) {
     case 8000:
         SampleRate = LVM_FS_8000;
+        pContext->pBundledContext->SamplesPerSecond = 8000*2; // 2 secs Stereo
         break;
     case 16000:
         SampleRate = LVM_FS_16000;
+        pContext->pBundledContext->SamplesPerSecond = 16000*2; // 2 secs Stereo
         break;
     case 22050:
         SampleRate = LVM_FS_22050;
+        pContext->pBundledContext->SamplesPerSecond = 22050*2; // 2 secs Stereo
         break;
     case 32000:
         SampleRate = LVM_FS_32000;
+        pContext->pBundledContext->SamplesPerSecond = 32000*2; // 2 secs Stereo
         break;
     case 44100:
         SampleRate = LVM_FS_44100;
+        pContext->pBundledContext->SamplesPerSecond = 44100*2; // 2 secs Stereo
         break;
     case 48000:
         SampleRate = LVM_FS_48000;
+        pContext->pBundledContext->SamplesPerSecond = 48000*2; // 2 secs Stereo
         break;
     default:
         LOGV("\tEffect_Configure invalid sampling rate %d", pConfig->inputCfg.samplingRate);
@@ -881,6 +938,7 @@
 
         LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "Effect_configure")
         LOGV("\tEffect_configure Succesfully called LVM_SetControlParameters\n");
+        pContext->pBundledContext->SampleRate = SampleRate;
 
     }else{
         //LOGV("\tEffect_configure keep sampling rate at %d", SampleRate);
@@ -1461,7 +1519,7 @@
     LVM_ReturnStatus_en     LvmStatus=LVM_SUCCESS;     /* Function call status */
     LVM_INT16               Balance = 0;
 
-    
+
 
     pContext->pBundledContext->positionSaved = position;
     Balance = VolumeConvertStereoPosition(pContext->pBundledContext->positionSaved);
@@ -1614,11 +1672,12 @@
 //----------------------------------------------------------------------------
 
 int BassBoost_getParameter(EffectContext     *pContext,
-                           int32_t           *pParam,
+                           void              *pParam,
                            size_t            *pValueSize,
                            void              *pValue){
     int status = 0;
-    int32_t param = *pParam++;
+    int32_t *pParamTemp = (int32_t *)pParam;
+    int32_t param = *pParamTemp++;
     int32_t param2;
     char *name;
 
@@ -1626,9 +1685,15 @@
 
     switch (param){
         case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
+            if (*pValueSize != sizeof(uint32_t)){
+                LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(uint32_t);
+            break;
         case BASSBOOST_PARAM_STRENGTH:
             if (*pValueSize != sizeof(int16_t)){
-                LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize2 %d", *pValueSize);
+                LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize %d", *pValueSize);
                 return -EINVAL;
             }
             *pValueSize = sizeof(int16_t);
@@ -1679,13 +1744,14 @@
 //
 //----------------------------------------------------------------------------
 
-int BassBoost_setParameter (EffectContext *pContext, int32_t *pParam, void *pValue){
+int BassBoost_setParameter (EffectContext *pContext, void *pParam, void *pValue){
     int status = 0;
     int16_t strength;
+    int32_t *pParamTemp = (int32_t *)pParam;
 
     //LOGV("\tBassBoost_setParameter start");
 
-    switch (*pParam){
+    switch (*pParamTemp){
         case BASSBOOST_PARAM_STRENGTH:
             strength = *(int16_t *)pValue;
             //LOGV("\tBassBoost_setParameter() BASSBOOST_PARAM_STRENGTH value is %d", strength);
@@ -1694,7 +1760,7 @@
             //LOGV("\tBassBoost_setParameter() Called pBassBoost->BassSetStrength");
            break;
         default:
-            LOGV("\tLVM_ERROR : BassBoost_setParameter() invalid param %d", *pParam);
+            LOGV("\tLVM_ERROR : BassBoost_setParameter() invalid param %d", *pParamTemp);
             break;
     }
 
@@ -1724,11 +1790,12 @@
 //----------------------------------------------------------------------------
 
 int Virtualizer_getParameter(EffectContext        *pContext,
-                             int32_t              *pParam,
+                             void                 *pParam,
                              size_t               *pValueSize,
                              void                 *pValue){
     int status = 0;
-    int32_t param = *pParam++;
+    int32_t *pParamTemp = (int32_t *)pParam;
+    int32_t param = *pParamTemp++;
     int32_t param2;
     char *name;
 
@@ -1736,9 +1803,16 @@
 
     switch (param){
         case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
+            if (*pValueSize != sizeof(uint32_t)){
+                LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(uint32_t);
+            break;
+
         case VIRTUALIZER_PARAM_STRENGTH:
             if (*pValueSize != sizeof(int16_t)){
-                LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize2 %d",*pValueSize);
+                LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
                 return -EINVAL;
             }
             *pValueSize = sizeof(int16_t);
@@ -1789,13 +1863,15 @@
 //
 //----------------------------------------------------------------------------
 
-int Virtualizer_setParameter (EffectContext *pContext, int32_t *pParam, void *pValue){
+int Virtualizer_setParameter (EffectContext *pContext, void *pParam, void *pValue){
     int status = 0;
     int16_t strength;
+    int32_t *pParamTemp = (int32_t *)pParam;
+    int32_t param = *pParamTemp++;
 
     //LOGV("\tVirtualizer_setParameter start");
 
-    switch (*pParam){
+    switch (param){
         case VIRTUALIZER_PARAM_STRENGTH:
             strength = *(int16_t *)pValue;
             //LOGV("\tVirtualizer_setParameter() VIRTUALIZER_PARAM_STRENGTH value is %d", strength);
@@ -1804,7 +1880,7 @@
             //LOGV("\tVirtualizer_setParameter() Called pVirtualizer->setStrength");
            break;
         default:
-            LOGV("\tLVM_ERROR : Virtualizer_setParameter() invalid param %d", *pParam);
+            LOGV("\tLVM_ERROR : Virtualizer_setParameter() invalid param %d", param);
             break;
     }
 
@@ -1833,12 +1909,13 @@
 //
 //----------------------------------------------------------------------------
 int Equalizer_getParameter(EffectContext     *pContext,
-                           int32_t           *pParam,
+                           void              *pParam,
                            size_t            *pValueSize,
                            void              *pValue){
     int status = 0;
     int bMute = 0;
-    int32_t param = *pParam++;
+    int32_t *pParamTemp = (int32_t *)pParam;
+    int32_t param = *pParamTemp++;
     int32_t param2;
     char *name;
 
@@ -1911,7 +1988,7 @@
         break;
 
     case EQ_PARAM_BAND_LEVEL:
-        param2 = *pParam;
+        param2 = *pParamTemp;
         if (param2 >= FIVEBAND_NUMBANDS) {
             status = -EINVAL;
             break;
@@ -1922,7 +1999,7 @@
         break;
 
     case EQ_PARAM_CENTER_FREQ:
-        param2 = *pParam;
+        param2 = *pParamTemp;
         if (param2 >= FIVEBAND_NUMBANDS) {
             status = -EINVAL;
             break;
@@ -1933,7 +2010,7 @@
         break;
 
     case EQ_PARAM_BAND_FREQ_RANGE:
-        param2 = *pParam;
+        param2 = *pParamTemp;
         if (param2 >= FIVEBAND_NUMBANDS) {
             status = -EINVAL;
             break;
@@ -1944,7 +2021,7 @@
         break;
 
     case EQ_PARAM_GET_BAND:
-        param2 = *pParam;
+        param2 = *pParamTemp;
         *(uint16_t *)pValue = (uint16_t)EqualizerGetBand(pContext, param2);
         //LOGV("\tEqualizer_getParameter() EQ_PARAM_GET_BAND frequency %d, band %d",
         //      param2, *(int32_t *)pValue);
@@ -1961,7 +2038,7 @@
         break;
 
     case EQ_PARAM_GET_PRESET_NAME:
-        param2 = *pParam;
+        param2 = *pParamTemp;
         if (param2 >= EqualizerGetNumPresets()) {
         //if (param2 >= 20) {     // AGO FIX
             status = -EINVAL;
@@ -2009,12 +2086,14 @@
 // Outputs:
 //
 //----------------------------------------------------------------------------
-int Equalizer_setParameter (EffectContext *pContext, int32_t *pParam, void *pValue){
+int Equalizer_setParameter (EffectContext *pContext, void *pParam, void *pValue){
     int status = 0;
     int32_t preset;
     int32_t band;
     int32_t level;
-    int32_t param = *pParam++;
+    int32_t *pParamTemp = (int32_t *)pParam;
+    int32_t param = *pParamTemp++;
+
 
     //LOGV("\tEqualizer_setParameter start");
     switch (param) {
@@ -2029,7 +2108,7 @@
         EqualizerSetPreset(pContext, preset);
         break;
     case EQ_PARAM_BAND_LEVEL:
-        band =  *pParam;
+        band =  *pParamTemp;
         level = (int32_t)(*(int16_t *)pValue);
         //LOGV("\tEqualizer_setParameter() EQ_PARAM_BAND_LEVEL band %d, level %d", band, level);
         if (band >= FIVEBAND_NUMBANDS) {
@@ -2089,13 +2168,13 @@
 //----------------------------------------------------------------------------
 
 int Volume_getParameter(EffectContext     *pContext,
-                        int32_t           *pParam,
+                        void              *pParam,
                         size_t            *pValueSize,
                         void              *pValue){
     int status = 0;
     int bMute = 0;
-    int32_t param = *pParam++;
-    int32_t param2;
+    int32_t *pParamTemp = (int32_t *)pParam;
+    int32_t param = *pParamTemp++;;
     char *name;
 
     LOGV("\tVolume_getParameter start");
@@ -2182,16 +2261,18 @@
 //
 //----------------------------------------------------------------------------
 
-int Volume_setParameter (EffectContext *pContext, int32_t *pParam, void *pValue){
+int Volume_setParameter (EffectContext *pContext, void *pParam, void *pValue){
     int      status = 0;
     int16_t  level;
     int16_t  position;
     uint32_t mute;
     uint32_t positionEnabled;
+    int32_t *pParamTemp = (int32_t *)pParam;
+    int32_t param = *pParamTemp++;
 
     LOGV("\tVolume_setParameter start");
 
-    switch (*pParam){
+    switch (param){
         case VOLUME_PARAM_LEVEL:
             level = *(int16_t *)pValue;
             LOGV("\tVolume_setParameter() VOLUME_PARAM_LEVEL value is %d", level);
@@ -2224,7 +2305,7 @@
             break;
 
         default:
-            LOGV("\tLVM_ERROR : Volume_setParameter() invalid param %d", *pParam);
+            LOGV("\tLVM_ERROR : Volume_setParameter() invalid param %d", param);
             break;
     }
 
@@ -2286,13 +2367,27 @@
                               audio_buffer_t         *inBuffer,
                               audio_buffer_t         *outBuffer){
     EffectContext * pContext = (EffectContext *) self;
+    LVM_ControlParams_t     ActiveParams;                           /* Current control Parameters */
+    LVM_ReturnStatus_en     LvmStatus = LVM_SUCCESS;                /* Function call status */
     int    status = 0;
+    int    status2Sec = 0;
     int    lvmStatus = 0;
     LVM_INT16   *in  = (LVM_INT16 *)inBuffer->raw;
     LVM_INT16   *out = (LVM_INT16 *)outBuffer->raw;
 
     //LOGV("\tEffect_process Start : Enabled = %d     Called = %d",
     //pContext->pBundledContext->NumberEffectsEnabled,pContext->pBundledContext->NumberEffectsCalled);
+//    LOGV("\tEffect_process Start : Samples left %d %d %d",
+//    pContext->pBundledContext->SamplesToExitCountBb,
+//    pContext->pBundledContext->SamplesToExitCountVirt,
+//    pContext->pBundledContext->SamplesToExitCountEq);
+
+//    LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
+//    LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "VolumeGetStereoPosition")
+//    if(LvmStatus != LVM_SUCCESS) return -EINVAL;
+//    LOGV("\tEffect_process Internal Operating Modes: BB %d   VIRT %d    EQ %d",
+//        ActiveParams.BE_OperatingMode, ActiveParams.VirtualizerOperatingMode,
+//        ActiveParams.EQNB_OperatingMode);
 
     if (pContext == NULL){
         LOGV("\tLVM_ERROR : Effect_process() ERROR pContext == NULL");
@@ -2306,23 +2401,44 @@
     }
     if ((pContext->pBundledContext->bBassEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_BASS_BOOST)){
-        LOGV("\tEffect_process() ERROR LVM_BASS_BOOST Effect is not enabled");
+        //LOGV("\tEffect_process() LVM_BASS_BOOST Effect is not enabled");
+        if(pContext->pBundledContext->SamplesToExitCountBb > 0){
+            status2Sec = -ENODATA;
+            pContext->pBundledContext->SamplesToExitCountBb -= outBuffer->frameCount * 2; // STEREO
+            //LOGV("\tEffect_process: Waiting for 2 secs to turn off BASS_BOOST, %d samples left",
+            //    pContext->pBundledContext->SamplesToExitCountBb);
+        } else {
         status = -ENODATA;
+        }
     }
     if ((pContext->pBundledContext->bVolumeEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_VOLUME)){
-        LOGV("\tEffect_process() ERROR LVM_VOLUME Effect is not enabled");
+        //LOGV("\tEffect_process() LVM_VOLUME Effect is not enabled");
         status = -ENODATA;
     }
     if ((pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_EQUALIZER)){
-        LOGV("\tEffect_process() ERROR LVM_EQUALIZER Effect is not enabled");
-        status = -ENODATA;
+        //LOGV("\tEffect_process() LVM_EQUALIZER Effect is not enabled");
+        if(pContext->pBundledContext->SamplesToExitCountEq > 0){
+            status2Sec = -ENODATA;
+            pContext->pBundledContext->SamplesToExitCountEq -= outBuffer->frameCount * 2; // STEREO
+            //LOGV("\tEffect_process: Waiting for 2 secs to turn off EQUALIZER, %d samples left",
+            //    pContext->pBundledContext->SamplesToExitCountEq);
+        } else {
+            status = -ENODATA;
+        }
     }
     if ((pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_VIRTUALIZER)){
-        LOGV("\tEffect_process() ERROR LVM_VIRTUALIZER Effect is not enabled");
-        status = -ENODATA;
+        //LOGV("\tEffect_process() LVM_VIRTUALIZER Effect is not enabled");
+        if(pContext->pBundledContext->SamplesToExitCountVirt > 0){
+            status2Sec = -ENODATA;
+            pContext->pBundledContext->SamplesToExitCountVirt -= outBuffer->frameCount * 2;// STEREO
+            //LOGV("\tEffect_process: Waiting for 2 secs to turn off VIRTUALIZER, %d samples left",
+            //    pContext->pBundledContext->SamplesToExitCountVirt);
+        } else {
+            status = -ENODATA;
+        }
     }
 
     // If this is the last frame of an effect process its output with no effect
@@ -2339,7 +2455,7 @@
         }
     }
 
-    if(status != -ENODATA){
+    if((status2Sec != -ENODATA)&&(status != -ENODATA)){
         pContext->pBundledContext->NumberEffectsCalled++;
     }
 
@@ -2483,7 +2599,7 @@
                 int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
 
                 p->status = android::BassBoost_getParameter(pContext,
-                                                            (int32_t *)p->data,
+                                                            p->data,
                                                             (size_t  *)&p->vsize,
                                                             p->data + voffset);
 
@@ -2514,7 +2630,7 @@
                 int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
 
                 p->status = android::Virtualizer_getParameter(pContext,
-                                                             (int32_t *)p->data,
+                                                             (void *)p->data,
                                                              (size_t  *)&p->vsize,
                                                               p->data + voffset);
 
@@ -2545,8 +2661,10 @@
 
                 int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
 
-                p->status = android::Equalizer_getParameter(pContext, (int32_t *)p->data, &p->vsize,
-                        p->data + voffset);
+                p->status = android::Equalizer_getParameter(pContext,
+                                                            p->data,
+                                                            &p->vsize,
+                                                            p->data + voffset);
 
                 *replySize = sizeof(effect_param_t) + voffset + p->vsize;
 
@@ -2577,7 +2695,7 @@
                 int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
 
                 p->status = android::Volume_getParameter(pContext,
-                                                         (int32_t *)p->data,
+                                                         (void *)p->data,
                                                          (size_t  *)&p->vsize,
                                                          p->data + voffset);
 
@@ -2623,7 +2741,7 @@
                 //        cmdSize, sizeof(effect_param_t), p->psize, p->vsize );
 
                 *(int *)pReplyData = android::BassBoost_setParameter(pContext,
-                                                                    (int32_t *)p->data,
+                                                                    (void *)p->data,
                                                                     p->data + p->psize);
             }
             if(pContext->EffectType == LVM_VIRTUALIZER){
@@ -2656,7 +2774,7 @@
                 //        cmdSize, sizeof(effect_param_t), p->psize, p->vsize );
 
                 *(int *)pReplyData = android::Virtualizer_setParameter(pContext,
-                                                                      (int32_t *)p->data,
+                                                                      (void *)p->data,
                                                                        p->data + p->psize);
             }
             if(pContext->EffectType == LVM_EQUALIZER){
@@ -2676,7 +2794,7 @@
                 effect_param_t *p = (effect_param_t *) pCmdData;
 
                 *(int *)pReplyData = android::Equalizer_setParameter(pContext,
-                                                                    (int32_t *)p->data,
+                                                                    (void *)p->data,
                                                                      p->data + p->psize);
             }
             if(pContext->EffectType == LVM_VOLUME){
@@ -2698,14 +2816,14 @@
                 effect_param_t *p = (effect_param_t *) pCmdData;
 
                 *(int *)pReplyData = android::Volume_setParameter(pContext,
-                                                                 (int32_t *)p->data,
+                                                                 (void *)p->data,
                                                                  p->data + p->psize);
             }
             //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_SET_PARAM end");
         } break;
 
         case EFFECT_CMD_ENABLE:
-            //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE start");
+            LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE start");
             if (pReplyData == NULL || *replySize != sizeof(int)){
                 LOGV("\tLVM_ERROR : Effect_command cmdCode Case: EFFECT_CMD_ENABLE: ERROR");
                 return -EINVAL;
@@ -2755,6 +2873,14 @@
             *(int *)pReplyData = 0;
             pContext->pBundledContext->NumberEffectsEnabled++;
             android::LvmEffect_enable(pContext);
+            pContext->pBundledContext->SamplesToExitCountEq =
+                 (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*1); // 0.1 secs Stereo
+            pContext->pBundledContext->SamplesToExitCountBb =
+                 (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*6); // 2 secs Stereo
+            pContext->pBundledContext->SamplesToExitCountVirt =
+                 (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*1); // 2 secs Stereo
+            LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE Samples to Exit = %d",
+                pContext->pBundledContext->SamplesToExitCountBb);
             //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE NumberEffectsEnabled = %d",
             //        pContext->pBundledContext->NumberEffectsEnabled);
             //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE end");
@@ -2893,11 +3019,8 @@
         }
         case EFFECT_CMD_SET_VOLUME:
         {
-            int32_t channels = cmdSize/sizeof(int32_t);
             int32_t vol     = *(int32_t *)pCmdData;
-            int16_t vol_db;
             int16_t dB;
-            int16_t vol_db_rnd;
             int32_t vol_ret[2] = {1<<24,1<<24}; // Apply no volume
 
             // if pReplyData is NULL, VOL_CTRL is delegated to another effect
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index d009bf9..1bee974 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -58,6 +58,7 @@
 struct BundledEffectContext{
     LVM_Handle_t                    hInstance;                /* Instance handle */
     int                             SessionNo;                /* Current session number */
+    int                             SessionId;                /* Current session id */
     bool                            bVolumeEnabled;           /* Flag for Volume */
     bool                            bEqualizerEnabled;        /* Flag for EQ */
     bool                            bBassEnabled;             /* Flag for Bass */
@@ -80,6 +81,10 @@
     bool                            bStereoPositionEnabled;
     int                             frameCount;
     LVM_Fs_en                       SampleRate;
+    int                             SamplesPerSecond;
+    int                             SamplesToExitCountEq;
+    int                             SamplesToExitCountBb;
+    int                             SamplesToExitCountVirt;
     #ifdef LVM_PCM
     FILE                            *PcmInPtr;
     FILE                            *PcmOutPtr;
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
new file mode 100755
index 0000000..03f1409
--- /dev/null
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -0,0 +1,1965 @@
+/*
+ * Copyright (C) 2010-2010 NXP Software
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Reverb"
+#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
+//#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <new>
+#include <EffectReverb.h>
+#include <LVREV.h>
+
+#define MAX_NUM_BANDS           5
+#define MAX_CALL_SIZE           256
+#define LVREV_MAX_T60           7000
+#define LVREV_MAX_REVERB_LEVEL  2000
+
+//#define LVM_PCM
+
+// effect_interface_t interface implementation for reverb
+extern "C" const struct effect_interface_s gReverbInterface;
+
+#define LVM_ERROR_CHECK(LvmStatus, callingFunc, calledFunc){\
+        if (LvmStatus == LVREV_NULLADDRESS){\
+            LOGV("\tLVREV_ERROR : Parameter error - "\
+                    "null pointer returned by %s in %s\n\n\n\n", callingFunc, calledFunc);\
+        }\
+        if (LvmStatus == LVREV_INVALIDNUMSAMPLES){\
+            LOGV("\tLVREV_ERROR : Parameter error - "\
+                    "bad number of samples returned by %s in %s\n\n\n\n", callingFunc, calledFunc);\
+        }\
+        if (LvmStatus == LVREV_OUTOFRANGE){\
+            LOGV("\tLVREV_ERROR : Parameter error - "\
+                    "out of range returned by %s in %s\n", callingFunc, calledFunc);\
+        }\
+    }
+
+// Namespaces
+namespace android {
+namespace {
+
+/************************************************************************************/
+/*                                                                                  */
+/* Preset definitions                                                               */
+/*                                                                                  */
+/************************************************************************************/
+
+const static t_reverb_settings sReverbPresets[] = {
+        // REVERB_PRESET_NONE: values are unused
+        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        // REVERB_PRESET_SMALLROOM
+        {-1000, -600, 1100, 830, -400, 5, 500, 10, 1000, 1000},
+        // REVERB_PRESET_MEDIUMROOM
+        {-1000, -600, 1300, 830, -1000, 20, -200, 20, 1000, 1000},
+        // REVERB_PRESET_LARGEROOM
+        {-1000, -600, 1500, 830, -1600, 5, -1000, 40, 1000, 1000},
+        // REVERB_PRESET_MEDIUMHALL
+        {-1000, -600, 1800, 700, -1300, 15, -800, 30, 1000, 1000},
+        // REVERB_PRESET_LARGEHALL
+        {-1000, -600, 1800, 700, -2000, 30, -1400, 60, 1000, 1000},
+        // REVERB_PRESET_PLATE
+        {-1000, -200, 1300, 900, 0, 2, 0, 10, 1000, 750},
+};
+
+
+// NXP SW auxiliary environmental reverb
+const effect_descriptor_t gAuxEnvReverbDescriptor = {
+        { 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e } },
+        { 0x4a387fc0, 0x8ab3, 0x11df, 0x8bad, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } },
+        EFFECT_API_VERSION,
+        EFFECT_FLAG_TYPE_AUXILIARY,
+        0, // TODO
+        1,
+        "Auxiliary Environmental Reverb",
+        "NXP Software Ltd.",
+};
+
+// NXP SW insert environmental reverb
+static const effect_descriptor_t gInsertEnvReverbDescriptor = {
+        {0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}},
+        {0xc7a511a0, 0xa3bb, 0x11df, 0x860e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_API_VERSION,
+        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+        0, // TODO
+        1,
+        "Insert Environmental Reverb",
+        "NXP Software Ltd.",
+};
+
+// NXP SW auxiliary preset reverb
+static const effect_descriptor_t gAuxPresetReverbDescriptor = {
+        {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        {0xf29a1400, 0xa3bb, 0x11df, 0x8ddc, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_API_VERSION,
+        EFFECT_FLAG_TYPE_AUXILIARY,
+        0, // TODO
+        1,
+        "Auxiliary Preset Reverb",
+        "NXP Software Ltd.",
+};
+
+// NXP SW insert preset reverb
+static const effect_descriptor_t gInsertPresetReverbDescriptor = {
+        {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        {0x172cdf00, 0xa3bc, 0x11df, 0xa72f, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_API_VERSION,
+        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+        0, // TODO
+        1,
+        "Insert Preset Reverb",
+        "NXP Software Ltd.",
+};
+
+// gDescriptors contains pointers to all defined effect descriptor in this library
+static const effect_descriptor_t * const gDescriptors[] = {
+        &gAuxEnvReverbDescriptor,
+        &gInsertEnvReverbDescriptor,
+        &gAuxPresetReverbDescriptor,
+        &gInsertPresetReverbDescriptor
+};
+
+struct ReverbContext{
+    const struct effect_interface_s *itfe;
+    effect_config_t                 config;
+    LVREV_Handle_t                  hInstance;
+    int16_t                         SavedRoomLevel;
+    int16_t                         SavedHfLevel;
+    int16_t                         SavedDecayTime;
+    int16_t                         SavedDecayHfRatio;
+    int16_t                         SavedReverbLevel;
+    int16_t                         SavedDiffusion;
+    int16_t                         SavedDensity;
+    bool                            bEnabled;
+    #ifdef LVM_PCM
+    FILE                            *PcmInPtr;
+    FILE                            *PcmOutPtr;
+    #endif
+    LVM_Fs_en                       SampleRate;
+    bool                            auxiliary;
+    bool                            preset;
+    uint16_t                        curPreset;
+    uint16_t                        nextPreset;
+};
+
+#define REVERB_DEFAULT_PRESET REVERB_PRESET_MEDIUMROOM
+
+//--- local function prototypes
+int  Reverb_init            (ReverbContext *pContext);
+void Reverb_free            (ReverbContext *pContext);
+int  Reverb_configure       (ReverbContext *pContext, effect_config_t *pConfig);
+int  Reverb_setParameter    (ReverbContext *pContext, void *pParam, void *pValue);
+int  Reverb_getParameter    (ReverbContext *pContext,
+                             void          *pParam,
+                             size_t        *pValueSize,
+                             void          *pValue);
+int Reverb_LoadPreset       (ReverbContext   *pContext);
+
+/* Effect Library Interface Implementation */
+extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects){
+    LOGV("\n\tEffectQueryNumberEffects start");
+    *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
+    LOGV("\tEffectQueryNumberEffects creating %d effects", *pNumEffects);
+    LOGV("\tEffectQueryNumberEffects end\n");
+    return 0;
+}     /* end EffectQueryNumberEffects */
+
+extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor){
+    LOGV("\n\tEffectQueryEffect start");
+    LOGV("\tEffectQueryEffect processing index %d", index);
+    if (pDescriptor == NULL){
+        LOGV("\tLVM_ERROR : EffectQueryEffect was passed NULL pointer");
+        return -EINVAL;
+    }
+    if (index >= sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)) {
+        LOGV("\tLVM_ERROR : EffectQueryEffect index out of range %d", index);
+        return -ENOENT;
+    }
+    memcpy(pDescriptor, gDescriptors[index], sizeof(effect_descriptor_t));
+    LOGV("\tEffectQueryEffect end\n");
+    return 0;
+}     /* end EffectQueryEffect */
+
+extern "C" int EffectCreate(effect_uuid_t       *uuid,
+                            int32_t             sessionId,
+                            int32_t             ioId,
+                            effect_interface_t  *pInterface){
+    int ret;
+    int i;
+    int length = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
+    const effect_descriptor_t *desc;
+
+    LOGV("\t\nEffectCreate start");
+
+    if (pInterface == NULL || uuid == NULL){
+        LOGV("\tLVM_ERROR : EffectCreate() called with NULL pointer");
+        return -EINVAL;
+    }
+
+    for (i = 0; i < length; i++) {
+        desc = gDescriptors[i];
+        if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t))
+                == 0) {
+            break;
+        }
+    }
+
+    if (i == length) {
+        return -ENOENT;
+    }
+
+    ReverbContext *pContext = new ReverbContext;
+
+    pContext->itfe      = &gReverbInterface;
+    pContext->hInstance = NULL;
+
+    pContext->auxiliary = false;
+    if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY){
+        pContext->auxiliary = true;
+    }
+
+    pContext->preset = false;
+    if (memcmp(&desc->type, SL_IID_PRESETREVERB, sizeof(effect_uuid_t)) == 0) {
+        pContext->preset = true;
+        // force reloading preset at first call to process()
+        pContext->curPreset = REVERB_PRESET_LAST + 1;
+        pContext->nextPreset = REVERB_DEFAULT_PRESET;
+    }
+
+    LOGV("\tEffectCreate - Calling Reverb_init");
+    ret = Reverb_init(pContext);
+
+    if (ret < 0){
+        LOGV("\tLVM_ERROR : EffectCreate() init failed");
+        delete pContext;
+        return ret;
+    }
+
+    *pInterface = (effect_interface_t)pContext;
+
+    #ifdef LVM_PCM
+    pContext->PcmInPtr = NULL;
+    pContext->PcmOutPtr = NULL;
+
+    pContext->PcmInPtr  = fopen("/data/tmp/reverb_pcm_in.pcm", "w");
+    pContext->PcmOutPtr = fopen("/data/tmp/reverb_pcm_out.pcm", "w");
+
+    if((pContext->PcmInPtr  == NULL)||
+       (pContext->PcmOutPtr == NULL)){
+       return -EINVAL;
+    }
+    #endif
+
+    LOGV("\tEffectCreate %p, size %d", pContext, sizeof(ReverbContext));
+    LOGV("\tEffectCreate end\n");
+    return 0;
+} /* end EffectCreate */
+
+extern "C" int EffectRelease(effect_interface_t interface){
+    ReverbContext * pContext = (ReverbContext *)interface;
+
+    LOGV("\tEffectRelease %p", interface);
+    if (pContext == NULL){
+        LOGV("\tLVM_ERROR : EffectRelease called with NULL pointer");
+        return -EINVAL;
+    }
+
+    #ifdef LVM_PCM
+    fclose(pContext->PcmInPtr);
+    fclose(pContext->PcmOutPtr);
+    #endif
+    Reverb_free(pContext);
+    delete pContext;
+    return 0;
+} /* end EffectRelease */
+
+/* local functions */
+#define CHECK_ARG(cond) {                     \
+    if (!(cond)) {                            \
+        LOGV("\tLVM_ERROR : Invalid argument: "#cond);      \
+        return -EINVAL;                       \
+    }                                         \
+}
+
+//----------------------------------------------------------------------------
+// MonoTo2I_32()
+//----------------------------------------------------------------------------
+// Purpose:
+//  Convert MONO to STEREO
+//
+//----------------------------------------------------------------------------
+
+void MonoTo2I_32( const LVM_INT32  *src,
+                        LVM_INT32  *dst,
+                        LVM_INT16 n)
+{
+   LVM_INT16 ii;
+   src += (n-1);
+   dst += ((n*2)-1);
+
+   for (ii = n; ii != 0; ii--)
+   {
+       *dst = *src;
+       dst--;
+
+       *dst = *src;
+       dst--;
+       src--;
+   }
+
+   return;
+}
+
+//----------------------------------------------------------------------------
+// From2iToMono_32()
+//----------------------------------------------------------------------------
+// Purpose:
+//  Convert STEREO to MONO
+//
+//----------------------------------------------------------------------------
+
+void From2iToMono_32( const LVM_INT32 *src,
+                            LVM_INT32 *dst,
+                            LVM_INT16 n)
+{
+   LVM_INT16 ii;
+   LVM_INT32 Temp;
+
+   for (ii = n; ii != 0; ii--)
+   {
+       Temp = (*src>>1);
+       src++;
+
+       Temp +=(*src>>1);
+       src++;
+
+       *dst = Temp;
+       dst++;
+   }
+
+   return;
+}
+
+static inline int16_t clamp16(int32_t sample)
+{
+    if ((sample>>15) ^ (sample>>31))
+        sample = 0x7FFF ^ (sample>>31);
+    return sample;
+}
+
+//----------------------------------------------------------------------------
+// process()
+//----------------------------------------------------------------------------
+// Purpose:
+// Apply the Reverb
+//
+// Inputs:
+//  pIn:        pointer to stereo/mono 16 bit input data
+//  pOut:       pointer to stereo 16 bit output data
+//  frameCount: Frames to process
+//  pContext:   effect engine context
+//  strength    strength to be applied
+//
+//  Outputs:
+//  pOut:       pointer to updated stereo 16 bit output data
+//
+//----------------------------------------------------------------------------
+
+int process( LVM_INT16     *pIn,
+             LVM_INT16     *pOut,
+             int           frameCount,
+             ReverbContext *pContext){
+
+    LVM_INT16               samplesPerFrame = 0;
+    LVREV_ReturnStatus_en   LvmStatus = LVREV_SUCCESS;              /* Function call status */
+
+    LVM_INT32 *InFrames32;
+    LVM_INT32 *OutFrames32;
+    LVM_INT16 *OutFrames16;
+
+
+    // Check that the input is either mono or stereo
+    if(pContext->config.inputCfg.channels == CHANNEL_STEREO){
+        samplesPerFrame = 2;
+    } else if (pContext->config.inputCfg.channels == CHANNEL_MONO){
+        samplesPerFrame = 1;
+    } else {
+        LOGV("\tLVREV_ERROR : process invalid PCM format");
+        return -EINVAL;
+    }
+
+    InFrames32  = (LVM_INT32 *)malloc(frameCount * sizeof(LVM_INT32) * 2);
+    OutFrames32 = (LVM_INT32 *)malloc(frameCount * sizeof(LVM_INT32) * 2);
+    OutFrames16 = (LVM_INT16 *)OutFrames32;
+
+    // Check for NULL pointers
+    if((InFrames32 == NULL)||(OutFrames32 == NULL)){
+        LOGV("\tLVREV_ERROR : process failed to allocate memory for temporary buffers ");
+        return -EINVAL;
+    }
+
+    #ifdef LVM_PCM
+    fwrite(pIn, frameCount*sizeof(LVM_INT16)*samplesPerFrame, 1, pContext->PcmInPtr);
+    fflush(pContext->PcmInPtr);
+    #endif
+
+    if (pContext->preset && pContext->nextPreset != pContext->curPreset) {
+        Reverb_LoadPreset(pContext);
+    }
+    // Convert to Input 32 bits
+    for(int i=0; i<frameCount*samplesPerFrame; i++){
+        InFrames32[i] = (LVM_INT32)pIn[i]<<8;
+    }
+
+     // If the input was MONO, convert to STEREO
+    if(pContext->config.inputCfg.channels == CHANNEL_MONO){
+        //LOGV("\tConverting Output from MONO to STEREO");
+        MonoTo2I_32(InFrames32, InFrames32, frameCount);
+    }
+
+    //LOGV("\tProcess, frames: %d, InFormat: %d(MONO=%d), OutFormat: %d(STEREO=%d)",
+    //frameCount, pContext->config.inputCfg.channels, CHANNEL_MONO,
+    //pContext->config.outputCfg.channels, CHANNEL_STEREO);
+
+    if (pContext->preset && pContext->curPreset == REVERB_PRESET_NONE) {
+        memset(OutFrames32, 0, frameCount * sizeof(LVM_INT32) * 2);
+    } else {
+    /* Process the samples */
+    LvmStatus = LVREV_Process(pContext->hInstance,      /* Instance handle */
+                              InFrames32,               /* Input buffer */
+                              OutFrames32,              /* Output buffer */
+                              frameCount);              /* Number of samples to read */
+    }
+
+    if (!pContext->auxiliary) {
+        for (int i=0; i<frameCount*2; i++){
+            OutFrames32[i] += InFrames32[i];
+        }
+    }
+
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_Process", "process")
+    if(LvmStatus != LVREV_SUCCESS) return -EINVAL;
+
+    // Convert to 16 bits
+    for(int i=0; i<frameCount*2; i++){  // Always stereo
+        OutFrames16[i] = clamp16(OutFrames32[i]>>8);
+    }
+
+    #ifdef LVM_PCM
+    fwrite(OutFrames16, frameCount*sizeof(LVM_INT16)*samplesPerFrame, 1, pContext->PcmOutPtr);
+    fflush(pContext->PcmOutPtr);
+    #endif
+
+    // Accumulate if required
+    if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
+        //LOGV("\tBuffer access is ACCUMULATE");
+        for (int i=0; i<frameCount*2; i++){
+            pOut[i] = clamp16((int32_t)pOut[i] + (int32_t)OutFrames16[i]);
+        }
+    }else{
+        //LOGV("\tBuffer access is WRITE");
+        memcpy(pOut, OutFrames16, frameCount*sizeof(LVM_INT16)*2); // 2 is for stereo output
+    }
+
+    free(InFrames32);
+    free(OutFrames32);
+
+    return 0;
+}    /* end process */
+
+//----------------------------------------------------------------------------
+// Reverb_free()
+//----------------------------------------------------------------------------
+// Purpose: Free all memory associated with the Bundle.
+//
+// Inputs:
+//  pContext:   effect engine context
+//
+// Outputs:
+//
+//----------------------------------------------------------------------------
+
+void Reverb_free(ReverbContext *pContext){
+
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;         /* Function call status */
+    LVREV_ControlParams_st    params;                        /* Control Parameters */
+    LVREV_MemoryTable_st      MemTab;
+
+    /* Free the algorithm memory */
+    LvmStatus = LVREV_GetMemoryTable(pContext->hInstance,
+                                   &MemTab,
+                                   LVM_NULL);
+
+    LVM_ERROR_CHECK(LvmStatus, "LVM_GetMemoryTable", "Reverb_free")
+
+    for (int i=0; i<LVM_NR_MEMORY_REGIONS; i++){
+        if (MemTab.Region[i].Size != 0){
+            if (MemTab.Region[i].pBaseAddress != NULL){
+                LOGV("\tfree() - START freeing %ld bytes for region %u at %p\n",
+                        MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
+
+                free(MemTab.Region[i].pBaseAddress);
+
+                LOGV("\tfree() - END   freeing %ld bytes for region %u at %p\n",
+                        MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
+            }else{
+                LOGV("\tLVM_ERROR : free() - trying to free with NULL pointer %ld bytes "
+                        "for region %u at %p ERROR\n",
+                        MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
+            }
+        }
+    }
+}    /* end Reverb_free */
+
+//----------------------------------------------------------------------------
+// Reverb_configure()
+//----------------------------------------------------------------------------
+// Purpose: Set input and output audio configuration.
+//
+// Inputs:
+//  pContext:   effect engine context
+//  pConfig:    pointer to effect_config_t structure holding input and output
+//      configuration parameters
+//
+// Outputs:
+//
+//----------------------------------------------------------------------------
+
+int Reverb_configure(ReverbContext *pContext, effect_config_t *pConfig){
+    LVM_Fs_en   SampleRate;
+    //LOGV("\tReverb_configure start");
+
+    CHECK_ARG(pContext != NULL);
+    CHECK_ARG(pConfig != NULL);
+
+    CHECK_ARG(pConfig->inputCfg.samplingRate == pConfig->outputCfg.samplingRate);
+    CHECK_ARG(pConfig->inputCfg.format == pConfig->outputCfg.format);
+    CHECK_ARG((pContext->auxiliary && pConfig->inputCfg.channels == CHANNEL_MONO) ||
+              ((!pContext->auxiliary) && pConfig->inputCfg.channels == CHANNEL_STEREO));
+    CHECK_ARG(pConfig->outputCfg.channels == CHANNEL_STEREO);
+    CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE
+              || pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
+    CHECK_ARG(pConfig->inputCfg.format == SAMPLE_FORMAT_PCM_S15);
+
+    if(pConfig->inputCfg.samplingRate != 44100){
+        return -EINVAL;
+    }
+
+    //LOGV("\tReverb_configure calling memcpy");
+    memcpy(&pContext->config, pConfig, sizeof(effect_config_t));
+
+    switch (pConfig->inputCfg.samplingRate) {
+    case 8000:
+        SampleRate = LVM_FS_8000;
+        break;
+    case 16000:
+        SampleRate = LVM_FS_16000;
+        break;
+    case 22050:
+        SampleRate = LVM_FS_22050;
+        break;
+    case 32000:
+        SampleRate = LVM_FS_32000;
+        break;
+    case 44100:
+        SampleRate = LVM_FS_44100;
+        break;
+    case 48000:
+        SampleRate = LVM_FS_48000;
+        break;
+    default:
+        LOGV("\rReverb_Configure invalid sampling rate %d", pConfig->inputCfg.samplingRate);
+        return -EINVAL;
+    }
+
+    if(pContext->SampleRate != SampleRate){
+
+        LVREV_ControlParams_st    ActiveParams;
+        LVREV_ReturnStatus_en     LvmStatus = LVREV_SUCCESS;
+
+        //LOGV("\tReverb_configure change sampling rate to %d", SampleRate);
+
+        /* Get the current settings */
+        LvmStatus = LVREV_GetControlParameters(pContext->hInstance,
+                                         &ActiveParams);
+
+        LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "Reverb_configure")
+        if(LvmStatus != LVREV_SUCCESS) return -EINVAL;
+
+        LvmStatus = LVREV_SetControlParameters(pContext->hInstance, &ActiveParams);
+
+        LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "Reverb_configure")
+        //LOGV("\tReverb_configure Succesfully called LVREV_SetControlParameters\n");
+
+    }else{
+        //LOGV("\tReverb_configure keep sampling rate at %d", SampleRate);
+    }
+
+    //LOGV("\tReverb_configure End");
+    return 0;
+}   /* end Reverb_configure */
+
+
+//----------------------------------------------------------------------------
+// Reverb_init()
+//----------------------------------------------------------------------------
+// Purpose: Initialize engine with default configuration
+//
+// Inputs:
+//  pContext:   effect engine context
+//
+// Outputs:
+//
+//----------------------------------------------------------------------------
+
+int Reverb_init(ReverbContext *pContext){
+    int status;
+
+    LOGV("\tReverb_init start");
+
+    CHECK_ARG(pContext != NULL);
+
+    if (pContext->hInstance != NULL){
+        Reverb_free(pContext);
+    }
+
+    pContext->config.inputCfg.accessMode                    = EFFECT_BUFFER_ACCESS_READ;
+    if (pContext->auxiliary) {
+        pContext->config.inputCfg.channels                  = CHANNEL_MONO;
+    } else {
+        pContext->config.inputCfg.channels                  = CHANNEL_STEREO;
+    }
+
+    pContext->config.inputCfg.format                        = SAMPLE_FORMAT_PCM_S15;
+    pContext->config.inputCfg.samplingRate                  = 44100;
+    pContext->config.inputCfg.bufferProvider.getBuffer      = NULL;
+    pContext->config.inputCfg.bufferProvider.releaseBuffer  = NULL;
+    pContext->config.inputCfg.bufferProvider.cookie         = NULL;
+    pContext->config.inputCfg.mask                          = EFFECT_CONFIG_ALL;
+    pContext->config.outputCfg.accessMode                   = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+    pContext->config.outputCfg.channels                     = CHANNEL_STEREO;
+    pContext->config.outputCfg.format                       = SAMPLE_FORMAT_PCM_S15;
+    pContext->config.outputCfg.samplingRate                 = 44100;
+    pContext->config.outputCfg.bufferProvider.getBuffer     = NULL;
+    pContext->config.outputCfg.bufferProvider.releaseBuffer = NULL;
+    pContext->config.outputCfg.bufferProvider.cookie        = NULL;
+    pContext->config.outputCfg.mask                         = EFFECT_CONFIG_ALL;
+
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;        /* Function call status */
+    LVREV_ControlParams_st    params;                         /* Control Parameters */
+    LVREV_InstanceParams_st   InstParams;                     /* Instance parameters */
+    LVREV_MemoryTable_st      MemTab;                         /* Memory allocation table */
+    bool                      bMallocFailure = LVM_FALSE;
+
+    /* Set the capabilities */
+    InstParams.MaxBlockSize  = MAX_CALL_SIZE;
+    InstParams.SourceFormat  = LVM_STEREO;
+    InstParams.NumDelays     = LVREV_DELAYLINES_4;
+
+    /* Allocate memory, forcing alignment */
+    LvmStatus = LVREV_GetMemoryTable(LVM_NULL,
+                                  &MemTab,
+                                  &InstParams);
+
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetMemoryTable", "Reverb_init")
+    if(LvmStatus != LVREV_SUCCESS) return -EINVAL;
+
+    LOGV("\tCreateInstance Succesfully called LVM_GetMemoryTable\n");
+
+    /* Allocate memory */
+    for (int i=0; i<LVM_NR_MEMORY_REGIONS; i++){
+        if (MemTab.Region[i].Size != 0){
+            MemTab.Region[i].pBaseAddress = malloc(MemTab.Region[i].Size);
+
+            if (MemTab.Region[i].pBaseAddress == LVM_NULL){
+                LOGV("\tLVREV_ERROR :Reverb_init CreateInstance Failed to allocate %ld bytes for region %u\n",
+                        MemTab.Region[i].Size, i );
+                bMallocFailure = LVM_TRUE;
+            }else{
+                LOGV("\tReverb_init CreateInstance allocate %ld bytes for region %u at %p\n",
+                        MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
+            }
+        }
+    }
+
+    /* If one or more of the memory regions failed to allocate, free the regions that were
+     * succesfully allocated and return with an error
+     */
+    if(bMallocFailure == LVM_TRUE){
+        for (int i=0; i<LVM_NR_MEMORY_REGIONS; i++){
+            if (MemTab.Region[i].pBaseAddress == LVM_NULL){
+                LOGV("\tLVM_ERROR :Reverb_init CreateInstance Failed to allocate %ld bytes for region %u"
+                     " - Not freeing\n", MemTab.Region[i].Size, i );
+            }else{
+                LOGV("\tLVM_ERROR :Reverb_init CreateInstance Failed: but allocated %ld bytes for region %u "
+                       "at %p- free\n", MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
+                free(MemTab.Region[i].pBaseAddress);
+            }
+        }
+        return -EINVAL;
+    }
+    LOGV("\tReverb_init CreateInstance Succesfully malloc'd memory\n");
+
+    /* Initialise */
+    pContext->hInstance = LVM_NULL;
+
+    /* Init sets the instance handle */
+    LvmStatus = LVREV_GetInstanceHandle(&pContext->hInstance,
+                                        &MemTab,
+                                        &InstParams);
+
+    LVM_ERROR_CHECK(LvmStatus, "LVM_GetInstanceHandle", "Reverb_init")
+    if(LvmStatus != LVREV_SUCCESS) return -EINVAL;
+
+    LOGV("\tReverb_init CreateInstance Succesfully called LVM_GetInstanceHandle\n");
+
+    /* Set the initial process parameters */
+    /* General parameters */
+    params.OperatingMode  = LVM_MODE_ON;
+    params.SampleRate     = LVM_FS_44100;
+    params.SourceFormat   = LVM_STEREO;
+
+    /* Reverb parameters */
+    params.Level          = 0;
+    params.LPF            = 23999;
+    params.HPF            = 50;
+    params.T60            = 1490;
+    params.Density        = 100;
+    params.Damping        = 21;
+    params.RoomSize       = 100;
+
+    /* Saved strength is used to return the exact strength that was used in the set to the get
+     * because we map the original strength range of 0:1000 to 1:15, and this will avoid
+     * quantisation like effect when returning
+     */
+    pContext->SavedRoomLevel    = -6000;
+    pContext->SavedHfLevel      = 0;
+    pContext->bEnabled          = LVM_FALSE;
+    pContext->SavedDecayTime    = params.T60;
+    pContext->SavedDecayHfRatio = params.Damping*10;
+    pContext->SavedDensity      = params.RoomSize*10;
+    pContext->SavedDiffusion    = params.Density*10;
+    pContext->SavedReverbLevel  = -6000;
+
+    /* Activate the initial settings */
+    LvmStatus = LVREV_SetControlParameters(pContext->hInstance,
+                                         &params);
+
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "Reverb_init")
+    if(LvmStatus != LVREV_SUCCESS) return -EINVAL;
+
+    LOGV("\tReverb_init CreateInstance Succesfully called LVREV_SetControlParameters\n");
+    LOGV("\tReverb_init End");
+    return 0;
+}   /* end Reverb_init */
+
+//----------------------------------------------------------------------------
+// ReverbConvertLevel()
+//----------------------------------------------------------------------------
+// Purpose:
+// Convert level from OpenSL ES format to LVM format
+//
+// Inputs:
+//  level       level to be applied
+//
+//----------------------------------------------------------------------------
+
+int16_t ReverbConvertLevel(int16_t level){
+    static int16_t LevelArray[101] =
+    {
+       -12000, -4000,  -3398,  -3046,  -2796,  -2603,  -2444,  -2310,  -2194,  -2092,
+       -2000,  -1918,  -1842,  -1773,  -1708,  -1648,  -1592,  -1540,  -1490,  -1443,
+       -1398,  -1356,  -1316,  -1277,  -1240,  -1205,  -1171,  -1138,  -1106,  -1076,
+       -1046,  -1018,  -990,   -963,   -938,   -912,   -888,   -864,   -841,   -818,
+       -796,   -775,   -754,   -734,   -714,   -694,   -675,   -656,   -638,   -620,
+       -603,   -585,   -568,   -552,   -536,   -520,   -504,   -489,   -474,   -459,
+       -444,   -430,   -416,   -402,   -388,   -375,   -361,   -348,   -335,   -323,
+       -310,   -298,   -286,   -274,   -262,   -250,   -239,   -228,   -216,   -205,
+       -194,   -184,   -173,   -162,   -152,   -142,   -132,   -121,   -112,   -102,
+       -92,    -82,    -73,    -64,    -54,    -45,    -36,    -27,    -18,    -9,
+       0
+    };
+    int16_t i;
+
+    for(i = 0; i < 101; i++)
+    {
+       if(level <= LevelArray[i])
+           break;
+    }
+    return i;
+}
+
+//----------------------------------------------------------------------------
+// ReverbConvertHFLevel()
+//----------------------------------------------------------------------------
+// Purpose:
+// Convert level from OpenSL ES format to LVM format
+//
+// Inputs:
+//  level       level to be applied
+//
+//----------------------------------------------------------------------------
+
+int16_t ReverbConvertHfLevel(int16_t Hflevel){
+    int16_t i;
+
+    static LPFPair_t LPFArray[97] =
+    {   // Limit range to 50 for LVREV parameter range
+        {-10000, 50}, { -5000, 50 }, { -4000, 50},  { -3000, 158}, { -2000, 502},
+        {-1000, 1666},{ -900, 1897}, { -800, 2169}, { -700, 2496}, { -600, 2895},
+        {-500, 3400}, { -400, 4066}, { -300, 5011}, { -200, 6537}, { -100,  9826},
+        {-99, 9881 }, { -98, 9937 }, { -97, 9994 }, { -96, 10052}, { -95, 10111},
+        {-94, 10171}, { -93, 10231}, { -92, 10293}, { -91, 10356}, { -90, 10419},
+        {-89, 10484}, { -88, 10549}, { -87, 10616}, { -86, 10684}, { -85, 10753},
+        {-84, 10823}, { -83, 10895}, { -82, 10968}, { -81, 11042}, { -80, 11117},
+        {-79, 11194}, { -78, 11272}, { -77, 11352}, { -76, 11433}, { -75, 11516},
+        {-74, 11600}, { -73, 11686}, { -72, 11774}, { -71, 11864}, { -70, 11955},
+        {-69, 12049}, { -68, 12144}, { -67, 12242}, { -66, 12341}, { -65, 12443},
+        {-64, 12548}, { -63, 12654}, { -62, 12763}, { -61, 12875}, { -60, 12990},
+        {-59, 13107}, { -58, 13227}, { -57, 13351}, { -56, 13477}, { -55, 13607},
+        {-54, 13741}, { -53, 13878}, { -52, 14019}, { -51, 14164}, { -50, 14313},
+        {-49, 14467}, { -48, 14626}, { -47, 14789}, { -46, 14958}, { -45, 15132},
+        {-44, 15312}, { -43, 15498}, { -42, 15691}, { -41, 15890}, { -40, 16097},
+        {-39, 16311}, { -38, 16534}, { -37, 16766}, { -36, 17007}, { -35, 17259},
+        {-34, 17521}, { -33, 17795}, { -32, 18081}, { -31, 18381}, { -30, 18696},
+        {-29, 19027}, { -28, 19375}, { -27, 19742}, { -26, 20129}, { -25, 20540},
+        {-24, 20976}, { -23, 21439}, { -22, 21934}, { -21, 22463}, { -20, 23031},
+        {-19, 23643}, { -18, 23999}
+    };
+
+    for(i = 0; i < 96; i++)
+    {
+        if(Hflevel <= LPFArray[i].Room_HF)
+            break;
+    }
+    return LPFArray[i].LPF;
+}
+
+//----------------------------------------------------------------------------
+// ReverbSetRoomHfLevel()
+//----------------------------------------------------------------------------
+// Purpose:
+// Apply the HF level to the Reverb. Must first be converted to LVM format
+//
+// Inputs:
+//  pContext:   effect engine context
+//  level       level to be applied
+//
+//----------------------------------------------------------------------------
+
+void ReverbSetRoomHfLevel(ReverbContext *pContext, int16_t level){
+    //LOGV("\tReverbSetRoomHfLevel start (%d)", level);
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbSetRoomHfLevel")
+    //LOGV("\tReverbSetRoomHfLevel Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbSetRoomHfLevel() just Got -> %d\n", ActiveParams.LPF);
+
+    ActiveParams.LPF = ReverbConvertHfLevel(level);
+
+    /* Activate the initial settings */
+    LvmStatus = LVREV_SetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "ReverbSetRoomHfLevel")
+    //LOGV("\tReverbSetRoomhfLevel() just Set -> %d\n", ActiveParams.LPF);
+    pContext->SavedHfLevel = level;
+    //LOGV("\tReverbSetHfRoomLevel end.. saving %d", pContext->SavedHfLevel);
+    return;
+}
+
+//----------------------------------------------------------------------------
+// ReverbGetRoomHfLevel()
+//----------------------------------------------------------------------------
+// Purpose:
+// Get the level applied to the Revervb. Must first be converted to LVM format
+//
+// Inputs:
+//  pContext:   effect engine context
+//
+//----------------------------------------------------------------------------
+
+int16_t ReverbGetRoomHfLevel(ReverbContext *pContext){
+    int16_t level;
+    //LOGV("\tReverbGetRoomHfLevel start, saved level is %d", pContext->SavedHfLevel);
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbGetRoomHfLevel")
+    //LOGV("\tReverbGetRoomHfLevel Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbGetRoomHfLevel() just Got -> %d\n", ActiveParams.LPF);
+
+    level = ReverbConvertHfLevel(pContext->SavedHfLevel);
+
+    //LOGV("\tReverbGetRoomHfLevel() ActiveParams.LPFL %d, pContext->SavedHfLevel: %d, "
+    //     "converted level: %d\n", ActiveParams.LPF, pContext->SavedHfLevel, level);
+
+    if(ActiveParams.LPF != level){
+        LOGV("\tLVM_ERROR : (ignore at start up) ReverbGetRoomHfLevel() has wrong level -> %d %d\n",
+               ActiveParams.Level, level);
+    }
+
+    //LOGV("\tReverbGetRoomHfLevel end");
+    return pContext->SavedHfLevel;
+}
+
+//----------------------------------------------------------------------------
+// ReverbSetReverbLevel()
+//----------------------------------------------------------------------------
+// Purpose:
+// Apply the level to the Reverb. Must first be converted to LVM format
+//
+// Inputs:
+//  pContext:   effect engine context
+//  level       level to be applied
+//
+//----------------------------------------------------------------------------
+
+void ReverbSetReverbLevel(ReverbContext *pContext, int16_t level){
+    //LOGV("\n\tReverbSetReverbLevel start (%d)", level);
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+    LVM_INT32                 CombinedLevel;             // Sum of room and reverb level controls
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbSetReverbLevel")
+    //LOGV("\tReverbSetReverbLevel Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbSetReverbLevel just Got -> %d\n", ActiveParams.Level);
+
+    // needs to subtract max levels for both RoomLevel and ReverbLevel
+    CombinedLevel = (level + pContext->SavedRoomLevel)-LVREV_MAX_REVERB_LEVEL;
+    //LOGV("\tReverbSetReverbLevel() CombinedLevel is %d = %d + %d\n",
+    //      CombinedLevel, level, pContext->SavedRoomLevel);
+
+    ActiveParams.Level = ReverbConvertLevel(CombinedLevel);
+
+    //LOGV("\tReverbSetReverbLevel() Trying to set -> %d\n", ActiveParams.Level);
+
+    /* Activate the initial settings */
+    LvmStatus = LVREV_SetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "ReverbSetReverbLevel")
+    //LOGV("\tReverbSetReverbLevel() just Set -> %d\n", ActiveParams.Level);
+
+    pContext->SavedReverbLevel = level;
+    //LOGV("\tReverbSetReverbLevel end pContext->SavedReverbLevel is %d\n\n",
+    //     pContext->SavedReverbLevel);
+    return;
+}
+
+//----------------------------------------------------------------------------
+// ReverbGetReverbLevel()
+//----------------------------------------------------------------------------
+// Purpose:
+// Get the level applied to the Revervb. Must first be converted to LVM format
+//
+// Inputs:
+//  pContext:   effect engine context
+//
+//----------------------------------------------------------------------------
+
+int16_t ReverbGetReverbLevel(ReverbContext *pContext){
+    int16_t level;
+    //LOGV("\tReverbGetReverbLevel start");
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+    LVM_INT32                 CombinedLevel;             // Sum of room and reverb level controls
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbGetReverbLevel")
+    //LOGV("\tReverbGetReverbLevel Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbGetReverbLevel() just Got -> %d\n", ActiveParams.Level);
+
+    // needs to subtract max levels for both RoomLevel and ReverbLevel
+    CombinedLevel = (pContext->SavedReverbLevel + pContext->SavedRoomLevel)-LVREV_MAX_REVERB_LEVEL;
+
+    //LOGV("\tReverbGetReverbLevel() CombinedLevel is %d = %d + %d\n",
+    //CombinedLevel, pContext->SavedReverbLevel, pContext->SavedRoomLevel);
+    level = ReverbConvertLevel(CombinedLevel);
+
+    //LOGV("\tReverbGetReverbLevel(): ActiveParams.Level: %d, pContext->SavedReverbLevel: %d, "
+    //"pContext->SavedRoomLevel: %d, CombinedLevel: %d, converted level: %d\n",
+    //ActiveParams.Level, pContext->SavedReverbLevel,pContext->SavedRoomLevel, CombinedLevel,level);
+
+    if(ActiveParams.Level != level){
+        LOGV("\tLVM_ERROR : (ignore at start up) ReverbGetReverbLevel() has wrong level -> %d %d\n",
+                ActiveParams.Level, level);
+    }
+
+    //LOGV("\tReverbGetReverbLevel end\n");
+
+    return pContext->SavedReverbLevel;
+}
+
+//----------------------------------------------------------------------------
+// ReverbSetRoomLevel()
+//----------------------------------------------------------------------------
+// Purpose:
+// Apply the level to the Reverb. Must first be converted to LVM format
+//
+// Inputs:
+//  pContext:   effect engine context
+//  level       level to be applied
+//
+//----------------------------------------------------------------------------
+
+void ReverbSetRoomLevel(ReverbContext *pContext, int16_t level){
+    //LOGV("\tReverbSetRoomLevel start (%d)", level);
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+    LVM_INT32                 CombinedLevel;             // Sum of room and reverb level controls
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbSetRoomLevel")
+    //LOGV("\tReverbSetRoomLevel Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbSetRoomLevel() just Got -> %d\n", ActiveParams.Level);
+
+    // needs to subtract max levels for both RoomLevel and ReverbLevel
+    CombinedLevel = (level + pContext->SavedReverbLevel)-LVREV_MAX_REVERB_LEVEL;
+    ActiveParams.Level = ReverbConvertLevel(CombinedLevel);
+
+    /* Activate the initial settings */
+    LvmStatus = LVREV_SetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "ReverbSetRoomLevel")
+    //LOGV("\tReverbSetRoomLevel() just Set -> %d\n", ActiveParams.Level);
+
+    pContext->SavedRoomLevel = level;
+    //LOGV("\tReverbSetRoomLevel end");
+    return;
+}
+
+//----------------------------------------------------------------------------
+// ReverbGetRoomLevel()
+//----------------------------------------------------------------------------
+// Purpose:
+// Get the level applied to the Revervb. Must first be converted to LVM format
+//
+// Inputs:
+//  pContext:   effect engine context
+//
+//----------------------------------------------------------------------------
+
+int16_t ReverbGetRoomLevel(ReverbContext *pContext){
+    int16_t level;
+    //LOGV("\tReverbGetRoomLevel start");
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+    LVM_INT32                 CombinedLevel;             // Sum of room and reverb level controls
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbGetRoomLevel")
+    //LOGV("\tReverbGetRoomLevel Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbGetRoomLevel() just Got -> %d\n", ActiveParams.Level);
+
+    // needs to subtract max levels for both RoomLevel and ReverbLevel
+    CombinedLevel = (pContext->SavedRoomLevel + pContext->SavedReverbLevel-LVREV_MAX_REVERB_LEVEL);
+    level = ReverbConvertLevel(CombinedLevel);
+
+    //LOGV("\tReverbGetRoomLevel, Level = %d, pContext->SavedRoomLevel = %d, "
+    //     "pContext->SavedReverbLevel = %d, CombinedLevel = %d, level = %d",
+    //ActiveParams.Level, pContext->SavedRoomLevel, pContext->SavedReverbLevel, CombinedLevel, level);
+
+    if(ActiveParams.Level != level){
+        LOGV("\tLVM_ERROR : (ignore at start up) ReverbGetRoomLevel() has wrong level -> %d %d\n",
+              ActiveParams.Level, level);
+    }
+
+    //LOGV("\tReverbGetRoomLevel end");
+    return pContext->SavedRoomLevel;
+}
+
+//----------------------------------------------------------------------------
+// ReverbSetDecayTime()
+//----------------------------------------------------------------------------
+// Purpose:
+// Apply the decay time to the Reverb.
+//
+// Inputs:
+//  pContext:   effect engine context
+//  time        decay to be applied
+//
+//----------------------------------------------------------------------------
+
+void ReverbSetDecayTime(ReverbContext *pContext, uint32_t time){
+    //LOGV("\tReverbSetDecayTime start (%d)", time);
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbSetDecayTime")
+    //LOGV("\tReverbSetDecayTime Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbSetDecayTime() just Got -> %d\n", ActiveParams.T60);
+
+    if (time <= LVREV_MAX_T60) {
+        ActiveParams.T60 = time;
+    }
+    else {
+        ActiveParams.T60 = LVREV_MAX_T60;
+    }
+
+    /* Activate the initial settings */
+    LvmStatus = LVREV_SetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "ReverbSetDecayTime")
+    //LOGV("\tReverbSetDecayTime() just Set -> %d\n", ActiveParams.T60);
+
+    pContext->SavedDecayTime = time;
+    //LOGV("\tReverbSetDecayTime end");
+    return;
+}
+
+//----------------------------------------------------------------------------
+// ReverbGetDecayTime()
+//----------------------------------------------------------------------------
+// Purpose:
+// Get the decay time applied to the Revervb.
+//
+// Inputs:
+//  pContext:   effect engine context
+//
+//----------------------------------------------------------------------------
+
+int32_t ReverbGetDecayTime(ReverbContext *pContext){
+    //LOGV("\tReverbGetDecayTime start");
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbGetDecayTime")
+    //LOGV("\tReverbGetDecayTime Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbGetDecayTime() just Got -> %d\n", ActiveParams.T60);
+
+    if(ActiveParams.T60 != pContext->SavedDecayTime){
+        // This will fail if the decay time is set to more than 7000
+        LOGV("\tLVM_ERROR : ReverbGetDecayTime() has wrong level -> %d %d\n",
+         ActiveParams.T60, pContext->SavedDecayTime);
+    }
+
+    //LOGV("\tReverbGetDecayTime end");
+    return ActiveParams.T60;
+}
+
+//----------------------------------------------------------------------------
+// ReverbSetDecayHfRatio()
+//----------------------------------------------------------------------------
+// Purpose:
+// Apply the HF decay ratio to the Reverb.
+//
+// Inputs:
+//  pContext:   effect engine context
+//  ratio       ratio to be applied
+//
+//----------------------------------------------------------------------------
+
+void ReverbSetDecayHfRatio(ReverbContext *pContext, int16_t ratio){
+    //LOGV("\tReverbSetDecayHfRatioe start (%d)", ratio);
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;   /* Function call status */
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbSetDecayHfRatio")
+    //LOGV("\tReverbSetDecayHfRatio Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbSetDecayHfRatio() just Got -> %d\n", ActiveParams.Damping);
+
+    ActiveParams.Damping = (LVM_INT16)(ratio/10);
+
+    /* Activate the initial settings */
+    LvmStatus = LVREV_SetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "ReverbSetDecayHfRatio")
+    //LOGV("\tReverbSetDecayHfRatio() just Set -> %d\n", ActiveParams.Damping);
+
+    pContext->SavedDecayHfRatio = ratio;
+    //LOGV("\tReverbSetDecayHfRatio end");
+    return;
+}
+
+//----------------------------------------------------------------------------
+// ReverbGetDecayHfRatio()
+//----------------------------------------------------------------------------
+// Purpose:
+// Get the HF decay ratio applied to the Revervb.
+//
+// Inputs:
+//  pContext:   effect engine context
+//
+//----------------------------------------------------------------------------
+
+int32_t ReverbGetDecayHfRatio(ReverbContext *pContext){
+    //LOGV("\tReverbGetDecayHfRatio start");
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;   /* Function call status */
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbGetDecayHfRatio")
+    //LOGV("\tReverbGetDecayHfRatio Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbGetDecayHfRatio() just Got -> %d\n", ActiveParams.Damping);
+
+    if(ActiveParams.Damping != (LVM_INT16)(pContext->SavedDecayHfRatio / 10)){
+        LOGV("\tLVM_ERROR : ReverbGetDecayHfRatio() has wrong level -> %d %d\n",
+         ActiveParams.Damping, pContext->SavedDecayHfRatio);
+    }
+
+    //LOGV("\tReverbGetDecayHfRatio end");
+    return pContext->SavedDecayHfRatio;
+}
+
+//----------------------------------------------------------------------------
+// ReverbSetDiffusion()
+//----------------------------------------------------------------------------
+// Purpose:
+// Apply the diffusion to the Reverb.
+//
+// Inputs:
+//  pContext:   effect engine context
+//  level        decay to be applied
+//
+//----------------------------------------------------------------------------
+
+void ReverbSetDiffusion(ReverbContext *pContext, int16_t level){
+    //LOGV("\tReverbSetDiffusion start (%d)", level);
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbSetDiffusion")
+    //LOGV("\tReverbSetDiffusion Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbSetDiffusion() just Got -> %d\n", ActiveParams.Density);
+
+    ActiveParams.Density = (LVM_INT16)(level/10);
+
+    /* Activate the initial settings */
+    LvmStatus = LVREV_SetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "ReverbSetDiffusion")
+    //LOGV("\tReverbSetDiffusion() just Set -> %d\n", ActiveParams.Density);
+
+    pContext->SavedDiffusion = level;
+    //LOGV("\tReverbSetDiffusion end");
+    return;
+}
+
+//----------------------------------------------------------------------------
+// ReverbGetDiffusion()
+//----------------------------------------------------------------------------
+// Purpose:
+// Get the decay time applied to the Revervb.
+//
+// Inputs:
+//  pContext:   effect engine context
+//
+//----------------------------------------------------------------------------
+
+int32_t ReverbGetDiffusion(ReverbContext *pContext){
+    //LOGV("\tReverbGetDiffusion start");
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+    LVM_INT16                 Temp;
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbGetDiffusion")
+    //LOGV("\tReverbGetDiffusion Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbGetDiffusion just Got -> %d\n", ActiveParams.Density);
+
+    Temp = (LVM_INT16)(pContext->SavedDiffusion/10);
+
+    if(ActiveParams.Density != Temp){
+        LOGV("\tLVM_ERROR : ReverbGetDiffusion invalid value %d %d", Temp, ActiveParams.Density);
+    }
+
+    //LOGV("\tReverbGetDiffusion end");
+    return pContext->SavedDiffusion;
+}
+
+//----------------------------------------------------------------------------
+// ReverbSetDensity()
+//----------------------------------------------------------------------------
+// Purpose:
+// Apply the density level the Reverb.
+//
+// Inputs:
+//  pContext:   effect engine context
+//  level        decay to be applied
+//
+//----------------------------------------------------------------------------
+
+void ReverbSetDensity(ReverbContext *pContext, int16_t level){
+    //LOGV("\tReverbSetDensity start (%d)", level);
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbSetDensity")
+    //LOGV("\tReverbSetDensity Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbSetDensity just Got -> %d\n", ActiveParams.RoomSize);
+
+    ActiveParams.RoomSize = (LVM_INT16)(((level * 99) / 1000) + 1);
+
+    /* Activate the initial settings */
+    LvmStatus = LVREV_SetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "ReverbSetDensity")
+    //LOGV("\tReverbSetDensity just Set -> %d\n", ActiveParams.RoomSize);
+
+    pContext->SavedDensity = level;
+    //LOGV("\tReverbSetDensity end");
+    return;
+}
+
+//----------------------------------------------------------------------------
+// ReverbGetDensity()
+//----------------------------------------------------------------------------
+// Purpose:
+// Get the density level applied to the Revervb.
+//
+// Inputs:
+//  pContext:   effect engine context
+//
+//----------------------------------------------------------------------------
+
+int32_t ReverbGetDensity(ReverbContext *pContext){
+    //LOGV("\tReverbGetDensity start");
+
+    LVREV_ControlParams_st    ActiveParams;              /* Current control Parameters */
+    LVREV_ReturnStatus_en     LvmStatus=LVREV_SUCCESS;     /* Function call status */
+    LVM_INT16                 Temp;
+    /* Get the current settings */
+    LvmStatus = LVREV_GetControlParameters(pContext->hInstance, &ActiveParams);
+    LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "ReverbGetDensity")
+    //LOGV("\tReverbGetDensity Succesfully returned from LVM_GetControlParameters\n");
+    //LOGV("\tReverbGetDensity() just Got -> %d\n", ActiveParams.RoomSize);
+
+
+    Temp = (LVM_INT16)(((pContext->SavedDensity * 99) / 1000) + 1);
+
+    if(Temp != ActiveParams.RoomSize){
+        LOGV("\tLVM_ERROR : ReverbGetDensity invalid value %d %d", Temp, ActiveParams.RoomSize);
+    }
+
+    //LOGV("\tReverbGetDensity end");
+    return pContext->SavedDensity;
+}
+
+//----------------------------------------------------------------------------
+// Reverb_LoadPreset()
+//----------------------------------------------------------------------------
+// Purpose:
+// Load a the next preset
+//
+// Inputs:
+//  pContext         - handle to instance data
+//
+// Outputs:
+//
+// Side Effects:
+//
+//----------------------------------------------------------------------------
+int Reverb_LoadPreset(ReverbContext   *pContext)
+{
+    //TODO: add reflections delay, level and reverb delay when early reflections are
+    // implemented
+    pContext->curPreset = pContext->nextPreset;
+
+    if (pContext->curPreset != REVERB_PRESET_NONE) {
+        const t_reverb_settings *preset = &sReverbPresets[pContext->curPreset];
+        ReverbSetRoomLevel(pContext, preset->roomLevel);
+        ReverbSetRoomHfLevel(pContext, preset->roomHFLevel);
+        ReverbSetDecayTime(pContext, preset->decayTime);
+        ReverbSetDecayHfRatio(pContext, preset->decayHFRatio);
+        //reflectionsLevel
+        //reflectionsDelay
+        ReverbSetReverbLevel(pContext, preset->reverbLevel);
+        // reverbDelay
+        ReverbSetDiffusion(pContext, preset->diffusion);
+        ReverbSetDensity(pContext, preset->density);
+    }
+
+    return 0;
+}
+
+
+//----------------------------------------------------------------------------
+// Reverb_getParameter()
+//----------------------------------------------------------------------------
+// Purpose:
+// Get a Reverb parameter
+//
+// Inputs:
+//  pContext         - handle to instance data
+//  pParam           - pointer to parameter
+//  pValue           - pointer to variable to hold retrieved value
+//  pValueSize       - pointer to value size: maximum size as input
+//
+// Outputs:
+//  *pValue updated with parameter value
+//  *pValueSize updated with actual value size
+//
+//
+// Side Effects:
+//
+//----------------------------------------------------------------------------
+
+int Reverb_getParameter(ReverbContext *pContext,
+                        void          *pParam,
+                        size_t        *pValueSize,
+                        void          *pValue){
+    int status = 0;
+    int32_t *pParamTemp = (int32_t *)pParam;
+    int32_t param = *pParamTemp++;
+    char *name;
+    t_reverb_settings *pProperties;
+
+    //LOGV("\tReverb_getParameter start");
+    if (pContext->preset) {
+        if (param != REVERB_PARAM_PRESET || *pValueSize < sizeof(uint16_t)) {
+            return -EINVAL;
+        }
+
+        *(uint16_t *)pValue = pContext->nextPreset;
+        LOGV("get REVERB_PARAM_PRESET, preset %d", pContext->nextPreset);
+        return 0;
+    }
+
+    switch (param){
+        case REVERB_PARAM_ROOM_LEVEL:
+            if (*pValueSize != sizeof(int16_t)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize1 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(int16_t);
+            break;
+        case REVERB_PARAM_ROOM_HF_LEVEL:
+            if (*pValueSize != sizeof(int16_t)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize12 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(int16_t);
+            break;
+        case REVERB_PARAM_DECAY_TIME:
+            if (*pValueSize != sizeof(uint32_t)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize3 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(uint32_t);
+            break;
+        case REVERB_PARAM_DECAY_HF_RATIO:
+            if (*pValueSize != sizeof(int16_t)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize4 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(int16_t);
+            break;
+        case REVERB_PARAM_REFLECTIONS_LEVEL:
+            if (*pValueSize != sizeof(int16_t)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize5 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(int16_t);
+            break;
+        case REVERB_PARAM_REFLECTIONS_DELAY:
+            if (*pValueSize != sizeof(uint32_t)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize6 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(uint32_t);
+            break;
+        case REVERB_PARAM_REVERB_LEVEL:
+            if (*pValueSize != sizeof(int16_t)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize7 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(int16_t);
+            break;
+        case REVERB_PARAM_REVERB_DELAY:
+            if (*pValueSize != sizeof(uint32_t)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize8 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(uint32_t);
+            break;
+        case REVERB_PARAM_DIFFUSION:
+            if (*pValueSize != sizeof(int16_t)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize9 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(int16_t);
+            break;
+        case REVERB_PARAM_DENSITY:
+            if (*pValueSize != sizeof(int16_t)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize10 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(int16_t);
+            break;
+        case REVERB_PARAM_PROPERTIES:
+            if (*pValueSize != sizeof(t_reverb_settings)){
+                LOGV("\tLVM_ERROR : Reverb_getParameter() invalid pValueSize11 %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(t_reverb_settings);
+            break;
+
+        default:
+            LOGV("\tLVM_ERROR : Reverb_getParameter() invalid param %d", param);
+            return -EINVAL;
+    }
+
+    pProperties = (t_reverb_settings *) pValue;
+
+    switch (param){
+        case REVERB_PARAM_PROPERTIES:
+            pProperties->roomLevel = ReverbGetRoomLevel(pContext);
+            pProperties->roomHFLevel = ReverbGetRoomHfLevel(pContext);
+            pProperties->decayTime = ReverbGetDecayTime(pContext);
+            pProperties->decayHFRatio = ReverbGetDecayHfRatio(pContext);
+            pProperties->reflectionsLevel = 0;
+            pProperties->reflectionsDelay = 0;
+            pProperties->reverbDelay = 0;
+            pProperties->reverbLevel = ReverbGetReverbLevel(pContext);
+            pProperties->diffusion = ReverbGetDiffusion(pContext);
+            pProperties->density = ReverbGetDensity(pContext);
+
+            LOGV("\tReverb_getParameter() REVERB_PARAM_PROPERTIES Value is roomLevel        %d",
+                pProperties->roomLevel);
+            LOGV("\tReverb_getParameter() REVERB_PARAM_PROPERTIES Value is roomHFLevel      %d",
+                pProperties->roomHFLevel);
+            LOGV("\tReverb_getParameter() REVERB_PARAM_PROPERTIES Value is decayTime        %d",
+                pProperties->decayTime);
+            LOGV("\tReverb_getParameter() REVERB_PARAM_PROPERTIES Value is decayHFRatio     %d",
+                pProperties->decayHFRatio);
+            LOGV("\tReverb_getParameter() REVERB_PARAM_PROPERTIES Value is reflectionsLevel %d",
+                pProperties->reflectionsLevel);
+            LOGV("\tReverb_getParameter() REVERB_PARAM_PROPERTIES Value is reflectionsDelay %d",
+                pProperties->reflectionsDelay);
+            LOGV("\tReverb_getParameter() REVERB_PARAM_PROPERTIES Value is reverbDelay      %d",
+                pProperties->reverbDelay);
+            LOGV("\tReverb_getParameter() REVERB_PARAM_PROPERTIES Value is reverbLevel      %d",
+                pProperties->reverbLevel);
+            LOGV("\tReverb_getParameter() REVERB_PARAM_PROPERTIES Value is diffusion        %d",
+                pProperties->diffusion);
+            LOGV("\tReverb_getParameter() REVERB_PARAM_PROPERTIES Value is density          %d",
+                pProperties->density);
+            break;
+
+        case REVERB_PARAM_ROOM_LEVEL:
+            *(int16_t *)pValue = ReverbGetRoomLevel(pContext);
+
+            //LOGV("\tReverb_getParameter() REVERB_PARAM_ROOM_LEVEL Value is %d",
+            //        *(int16_t *)pValue);
+            break;
+        case REVERB_PARAM_ROOM_HF_LEVEL:
+            *(int16_t *)pValue = ReverbGetRoomHfLevel(pContext);
+
+            //LOGV("\tReverb_getParameter() REVERB_PARAM_ROOM_HF_LEVEL Value is %d",
+            //        *(int16_t *)pValue);
+            break;
+        case REVERB_PARAM_DECAY_TIME:
+            *(int32_t *)pValue = ReverbGetDecayTime(pContext);
+
+            //LOGV("\tReverb_getParameter() REVERB_PARAM_DECAY_TIME Value is %d",
+            //        *(int32_t *)pValue);
+            break;
+        case REVERB_PARAM_DECAY_HF_RATIO:
+            *(int16_t *)pValue = ReverbGetDecayHfRatio(pContext);
+
+            //LOGV("\tReverb_getParameter() REVERB_PARAM_DECAY_HF_RATION Value is %d",
+            //        *(int16_t *)pValue);
+            break;
+        case REVERB_PARAM_REVERB_LEVEL:
+             *(int16_t *)pValue = ReverbGetReverbLevel(pContext);
+
+            //LOGV("\tReverb_getParameter() REVERB_PARAM_REVERB_LEVEL Value is %d",
+            //        *(int16_t *)pValue);
+            break;
+        case REVERB_PARAM_DIFFUSION:
+            *(int16_t *)pValue = ReverbGetDiffusion(pContext);
+
+            //LOGV("\tReverb_getParameter() REVERB_PARAM_DECAY_DIFFUSION Value is %d",
+            //        *(int16_t *)pValue);
+            break;
+        case REVERB_PARAM_DENSITY:
+            *(uint16_t *)pValue = 0;
+            *(int16_t *)pValue = ReverbGetDensity(pContext);
+            //LOGV("\tReverb_getParameter() REVERB_PARAM_DENSITY Value is %d",
+            //        *(uint32_t *)pValue);
+            break;
+        case REVERB_PARAM_REFLECTIONS_LEVEL:
+            *(uint16_t *)pValue = 0;
+        case REVERB_PARAM_REFLECTIONS_DELAY:
+            *(uint32_t *)pValue = 0;
+        case REVERB_PARAM_REVERB_DELAY:
+            *(uint32_t *)pValue = 0;
+            break;
+
+        default:
+            LOGV("\tLVM_ERROR : Reverb_getParameter() invalid param %d", param);
+            status = -EINVAL;
+            break;
+    }
+
+    //LOGV("\tReverb_getParameter end");
+    return status;
+} /* end Reverb_getParameter */
+
+//----------------------------------------------------------------------------
+// Reverb_setParameter()
+//----------------------------------------------------------------------------
+// Purpose:
+// Set a Reverb parameter
+//
+// Inputs:
+//  pContext         - handle to instance data
+//  pParam           - pointer to parameter
+//  pValue           - pointer to value
+//
+// Outputs:
+//
+//----------------------------------------------------------------------------
+
+int Reverb_setParameter (ReverbContext *pContext, void *pParam, void *pValue){
+    int status = 0;
+    int16_t level;
+    uint32_t time;
+    t_reverb_settings *pProperties;
+    int32_t *pParamTemp = (int32_t *)pParam;
+    int32_t param = *pParamTemp++;
+
+    //LOGV("\tReverb_setParameter start");
+    if (pContext->preset) {
+        if (param != REVERB_PARAM_PRESET) {
+            return -EINVAL;
+        }
+
+        uint16_t preset = *(uint16_t *)pValue;
+        LOGV("set REVERB_PARAM_PRESET, preset %d", preset);
+        if (preset > REVERB_PRESET_LAST) {
+            return -EINVAL;
+        }
+        pContext->nextPreset = preset;
+    }
+
+    switch (param){
+        case REVERB_PARAM_PROPERTIES:
+            LOGV("\tReverb_setParameter() REVERB_PARAM_PROPERTIES");
+            pProperties = (t_reverb_settings *) pValue;
+            ReverbSetRoomLevel(pContext, pProperties->roomLevel);
+            ReverbSetRoomHfLevel(pContext, pProperties->roomHFLevel);
+            ReverbSetDecayTime(pContext, pProperties->decayTime);
+            ReverbSetDecayHfRatio(pContext, pProperties->decayHFRatio);
+            ReverbSetReverbLevel(pContext, pProperties->reverbLevel);
+            ReverbSetDiffusion(pContext, pProperties->diffusion);
+            ReverbSetDensity(pContext, pProperties->density);
+            break;
+        case REVERB_PARAM_ROOM_LEVEL:
+            level = *(int16_t *)pValue;
+            //LOGV("\tReverb_setParameter() REVERB_PARAM_ROOM_LEVEL value is %d", level);
+            //LOGV("\tReverb_setParameter() Calling ReverbSetRoomLevel");
+            ReverbSetRoomLevel(pContext, level);
+            //LOGV("\tReverb_setParameter() Called ReverbSetRoomLevel");
+           break;
+        case REVERB_PARAM_ROOM_HF_LEVEL:
+            level = *(int16_t *)pValue;
+            //LOGV("\tReverb_setParameter() REVERB_PARAM_ROOM_HF_LEVEL value is %d", level);
+            //LOGV("\tReverb_setParameter() Calling ReverbSetRoomHfLevel");
+            ReverbSetRoomHfLevel(pContext, level);
+            //LOGV("\tReverb_setParameter() Called ReverbSetRoomHfLevel");
+           break;
+        case REVERB_PARAM_DECAY_TIME:
+            time = *(uint32_t *)pValue;
+            //LOGV("\tReverb_setParameter() REVERB_PARAM_DECAY_TIME value is %d", time);
+            //LOGV("\tReverb_setParameter() Calling ReverbSetDecayTime");
+            ReverbSetDecayTime(pContext, time);
+            //LOGV("\tReverb_setParameter() Called ReverbSetDecayTime");
+           break;
+        case REVERB_PARAM_DECAY_HF_RATIO:
+            time = *(int16_t *)pValue;
+            //LOGV("\tReverb_setParameter() REVERB_PARAM_DECAY_HF_RATIO value is %d", time);
+            //LOGV("\tReverb_setParameter() Calling ReverbSetDecayHfRatio");
+            ReverbSetDecayHfRatio(pContext, time);
+            //LOGV("\tReverb_setParameter() Called ReverbSetDecayHfRatio");
+            break;
+         case REVERB_PARAM_REVERB_LEVEL:
+            level = *(int16_t *)pValue;
+            //LOGV("\tReverb_setParameter() REVERB_PARAM_REVERB_LEVEL value is %d", level);
+            //LOGV("\tReverb_setParameter() Calling ReverbSetReverbLevel");
+            ReverbSetReverbLevel(pContext, level);
+            //LOGV("\tReverb_setParameter() Called ReverbSetReverbLevel");
+           break;
+        case REVERB_PARAM_DIFFUSION:
+            time = *(int16_t *)pValue;
+            //LOGV("\tReverb_setParameter() REVERB_PARAM_DECAY_DIFFUSION value is %d", time);
+            //LOGV("\tReverb_setParameter() Calling ReverbSetDiffusion");
+            ReverbSetDiffusion(pContext, time);
+            //LOGV("\tReverb_setParameter() Called ReverbSetDiffusion");
+            break;
+        case REVERB_PARAM_DENSITY:
+            time = *(int16_t *)pValue;
+            //LOGV("\tReverb_setParameter() REVERB_PARAM_DECAY_DENSITY value is %d", time);
+            //LOGV("\tReverb_setParameter() Calling ReverbSetDensity");
+            ReverbSetDensity(pContext, time);
+            //LOGV("\tReverb_setParameter() Called ReverbSetDensity");
+            break;
+           break;
+        case REVERB_PARAM_REFLECTIONS_LEVEL:
+        case REVERB_PARAM_REFLECTIONS_DELAY:
+        case REVERB_PARAM_REVERB_DELAY:
+            break;
+        default:
+            LOGV("\tLVM_ERROR : Reverb_setParameter() invalid param %d", param);
+            break;
+    }
+
+    //LOGV("\tReverb_setParameter end");
+    return status;
+} /* end Reverb_setParameter */
+
+} // namespace
+} // namespace
+
+/* Effect Control Interface Implementation: Process */
+extern "C" int Reverb_process(effect_interface_t   self,
+                                 audio_buffer_t         *inBuffer,
+                                 audio_buffer_t         *outBuffer){
+    android::ReverbContext * pContext = (android::ReverbContext *) self;
+    int    status = 0;
+
+    if (pContext == NULL){
+        LOGV("\tLVM_ERROR : Reverb_process() ERROR pContext == NULL");
+        return -EINVAL;
+    }
+    if (inBuffer == NULL  || inBuffer->raw == NULL  ||
+            outBuffer == NULL || outBuffer->raw == NULL ||
+            inBuffer->frameCount != outBuffer->frameCount){
+        LOGV("\tLVM_ERROR : Reverb_process() ERROR NULL INPUT POINTER OR FRAME COUNT IS WRONG");
+        return -EINVAL;
+    }
+    if (pContext->bEnabled == LVM_FALSE){
+        LOGV("\tReverb_process() ERROR Effect is not enabled");
+        return -ENODATA;
+    }
+    //LOGV("\tReverb_process() Calling process with %d frames", outBuffer->frameCount);
+    /* Process all the available frames, block processing is handled internalLY by the LVM bundle */
+    status = process(    (LVM_INT16 *)inBuffer->raw,
+                         (LVM_INT16 *)outBuffer->raw,
+                                      outBuffer->frameCount,
+                                      pContext);
+
+    return status;
+}   /* end Reverb_process */
+
+/* Effect Control Interface Implementation: Command */
+extern "C" int Reverb_command(effect_interface_t  self,
+                              uint32_t            cmdCode,
+                              uint32_t            cmdSize,
+                              void                *pCmdData,
+                              uint32_t            *replySize,
+                              void                *pReplyData){
+    android::ReverbContext * pContext = (android::ReverbContext *) self;
+    int retsize;
+
+    if (pContext == NULL){
+        LOGV("\tLVM_ERROR : Reverb_command ERROR pContext == NULL");
+        return -EINVAL;
+    }
+
+    //LOGV("\tReverb_command INPUTS are: command %d cmdSize %d",cmdCode, cmdSize);
+
+    switch (cmdCode){
+        case EFFECT_CMD_INIT:
+            //LOGV("\tReverb_command cmdCode Case: "
+            //        "EFFECT_CMD_INIT start");
+
+            if (pReplyData == NULL || *replySize != sizeof(int)){
+                LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+                        "EFFECT_CMD_INIT: ERROR");
+                return -EINVAL;
+            }
+            *(int *) pReplyData = 0;
+            break;
+
+        case EFFECT_CMD_CONFIGURE:
+            //LOGV("\tReverb_command cmdCode Case: "
+            //        "EFFECT_CMD_CONFIGURE start");
+            if (pCmdData    == NULL||
+                cmdSize     != sizeof(effect_config_t)||
+                pReplyData  == NULL||
+                *replySize  != sizeof(int)){
+                LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+                        "EFFECT_CMD_CONFIGURE: ERROR");
+                return -EINVAL;
+            }
+            *(int *) pReplyData = Reverb_configure(pContext, (effect_config_t *) pCmdData);
+            break;
+
+        case EFFECT_CMD_RESET:
+            //LOGV("\tReverb_command cmdCode Case: "
+            //        "EFFECT_CMD_RESET start");
+            Reverb_configure(pContext, &pContext->config);
+            break;
+
+        case EFFECT_CMD_GET_PARAM:{
+            //LOGV("\tReverb_command cmdCode Case: "
+            //        "EFFECT_CMD_GET_PARAM start");
+            if (pCmdData == NULL ||
+                    cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
+                    pReplyData == NULL ||
+                    *replySize < (int) (sizeof(effect_param_t) + sizeof(int32_t))){
+                LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+                        "EFFECT_CMD_GET_PARAM: ERROR");
+                return -EINVAL;
+            }
+            effect_param_t *p = (effect_param_t *)pCmdData;
+
+            memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + p->psize);
+
+            p = (effect_param_t *)pReplyData;
+
+            int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+
+            p->status = android::Reverb_getParameter(pContext,
+                                                         (void *)p->data,
+                                                         (size_t  *)&p->vsize,
+                                                          p->data + voffset);
+
+            *replySize = sizeof(effect_param_t) + voffset + p->vsize;
+
+            //LOGV("\tReverb_command EFFECT_CMD_GET_PARAM "
+            //        "*pCmdData %d, *replySize %d, *pReplyData %d ",
+            //        *(int32_t *)((char *)pCmdData + sizeof(effect_param_t)),
+            //        *replySize,
+            //        *(int16_t *)((char *)pReplyData + sizeof(effect_param_t) + voffset));
+
+        } break;
+        case EFFECT_CMD_SET_PARAM:{
+
+            //LOGV("\tReverb_command cmdCode Case: "
+            //        "EFFECT_CMD_SET_PARAM start");
+            //LOGV("\tReverb_command EFFECT_CMD_SET_PARAM param %d, *replySize %d, value %d ",
+            //        *(int32_t *)((char *)pCmdData + sizeof(effect_param_t)),
+            //        *replySize,
+            //        *(int16_t *)((char *)pCmdData + sizeof(effect_param_t) + sizeof(int32_t)));
+
+            if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
+                    || pReplyData == NULL || *replySize != (int)sizeof(int32_t)) {
+                LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+                        "EFFECT_CMD_SET_PARAM: ERROR");
+                return -EINVAL;
+            }
+
+            effect_param_t *p = (effect_param_t *) pCmdData;
+
+            if (p->psize != sizeof(int32_t)){
+                LOGV("\t4LVM_ERROR : Reverb_command cmdCode Case: "
+                        "EFFECT_CMD_SET_PARAM: ERROR, psize is not sizeof(int32_t)");
+                return -EINVAL;
+            }
+
+            //LOGV("\tn5Reverb_command cmdSize is %d\n"
+            //        "\tsizeof(effect_param_t) is  %d\n"
+            //        "\tp->psize is %d\n"
+            //        "\tp->vsize is %d"
+            //        "\n",
+            //        cmdSize, sizeof(effect_param_t), p->psize, p->vsize );
+
+            *(int *)pReplyData = android::Reverb_setParameter(pContext,
+                                                             (void *)p->data,
+                                                              p->data + p->psize);
+        } break;
+
+        case EFFECT_CMD_ENABLE:
+            //LOGV("\tReverb_command cmdCode Case: "
+            //        "EFFECT_CMD_ENABLE start");
+
+            if (pReplyData == NULL || *replySize != sizeof(int)){
+                LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+                        "EFFECT_CMD_ENABLE: ERROR");
+                return -EINVAL;
+            }
+            if(pContext->bEnabled == LVM_TRUE){
+                 LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+                         "EFFECT_CMD_ENABLE: ERROR-Effect is already enabled");
+                 return -EINVAL;
+             }
+            *(int *)pReplyData = 0;
+            pContext->bEnabled = LVM_TRUE;
+            break;
+        case EFFECT_CMD_DISABLE:
+            //LOGV("\tReverb_command cmdCode Case: "
+            //        "EFFECT_CMD_DISABLE start");
+
+            if (pReplyData == NULL || *replySize != sizeof(int)){
+                LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+                        "EFFECT_CMD_DISABLE: ERROR");
+                return -EINVAL;
+            }
+            if(pContext->bEnabled == LVM_FALSE){
+                 LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+                         "EFFECT_CMD_DISABLE: ERROR-Effect is not yet enabled");
+                 return -EINVAL;
+             }
+            *(int *)pReplyData = 0;
+            pContext->bEnabled = LVM_FALSE;
+            break;
+
+        case EFFECT_CMD_SET_DEVICE:
+        case EFFECT_CMD_SET_VOLUME:
+        case EFFECT_CMD_SET_AUDIO_MODE:
+            //LOGV("\tReverb_command cmdCode Case: "
+            //        "EFFECT_CMD_SET_DEVICE/EFFECT_CMD_SET_VOLUME/EFFECT_CMD_SET_AUDIO_MODE start");
+            break;
+
+        default:
+            LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+                    "DEFAULT start %d ERROR",cmdCode);
+            return -EINVAL;
+    }
+
+    //LOGV("\tReverb_command end\n\n");
+    return 0;
+}    /* end Reverb_command */
+
+// effect_interface_t interface implementation for Reverb effect
+const struct effect_interface_s gReverbInterface = {
+    Reverb_process,
+    Reverb_command
+};    /* end gReverbInterface */
+
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.h b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.h
new file mode 100755
index 0000000..1ab423f
--- /dev/null
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EFFECTREVERB_H_
+#define ANDROID_EFFECTREVERB_H_
+
+#include <media/EffectEnvironmentalReverbApi.h>
+#include <media/EffectPresetReverbApi.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct _LPFPair_t
+{
+    int16_t Room_HF;
+    int16_t LPF;
+} LPFPair_t;
+#if __cplusplus
+}  // extern "C"
+#endif
+
+
+#endif /*ANDROID_EFFECTREVERB_H_*/
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 977e6be..b4d01f5 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -31,7 +31,8 @@
     IEffect.cpp \
     IEffectClient.cpp \
     AudioEffect.cpp \
-    Visualizer.cpp
+    Visualizer.cpp \
+    fixedfft.cpp.arm
 
 LOCAL_SHARED_LIBRARIES := \
 	libui libcutils libutils libbinder libsonivox libicuuc libexpat libsurfaceflinger_client libcamera_client
@@ -50,11 +51,7 @@
     $(JNI_H_INCLUDE) \
     $(call include-path-for, graphics corecg) \
     $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
-    external/speex/include \
-    external/speex/libspeex \
     external/icu4c/common \
     external/expat/lib
 
-LOCAL_STATIC_LIBRARIES := libspeex
-
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index 0f55b19d..9dfdcb0 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -22,12 +22,14 @@
 
 #include <media/IMediaPlayer.h>
 #include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
 
 namespace android {
 
 enum {
     DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
     SET_VIDEO_SURFACE,
+    SET_VIDEO_ISURFACE,
     PREPARE_ASYNC,
     START,
     STOP,
@@ -65,11 +67,20 @@
         remote()->transact(DISCONNECT, data, &reply);
     }
 
-    status_t setVideoSurface(const sp<ISurface>& surface)
+    status_t setVideoISurface(const sp<ISurface>& surface)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
         data.writeStrongBinder(surface->asBinder());
+        remote()->transact(SET_VIDEO_ISURFACE, data, &reply);
+        return reply.readInt32();
+    }
+
+    status_t setVideoSurface(const sp<Surface>& surface)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+        Surface::writeToParcel(surface, &data);
         remote()->transact(SET_VIDEO_SURFACE, data, &reply);
         return reply.readInt32();
     }
@@ -256,9 +267,15 @@
             disconnect();
             return NO_ERROR;
         } break;
-        case SET_VIDEO_SURFACE: {
+        case SET_VIDEO_ISURFACE: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
             sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder());
+            reply->writeInt32(setVideoISurface(surface));
+            return NO_ERROR;
+        } break;
+        case SET_VIDEO_SURFACE: {
+            CHECK_INTERFACE(IMediaPlayer, data, reply);
+            sp<Surface> surface = Surface::readFromParcel(data);
             reply->writeInt32(setVideoSurface(surface));
             return NO_ERROR;
         } break;
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 947ff34..f55a01e 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -19,7 +19,7 @@
 #define LOG_TAG "IMediaRecorder"
 #include <utils/Log.h>
 #include <binder/Parcel.h>
-#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
 #include <camera/ICamera.h>
 #include <media/IMediaRecorderClient.h>
 #include <media/IMediaRecorder.h>
@@ -69,12 +69,12 @@
         return reply.readInt32();
     }
 
-    status_t setPreviewSurface(const sp<ISurface>& surface)
+    status_t setPreviewSurface(const sp<Surface>& surface)
     {
         LOGV("setPreviewSurface(%p)", surface.get());
         Parcel data, reply;
         data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
-        data.writeStrongBinder(surface->asBinder());
+        Surface::writeToParcel(surface, &data);
         remote()->transact(SET_PREVIEW_SURFACE, data, &reply);
         return reply.readInt32();
     }
@@ -409,7 +409,7 @@
         case SET_PREVIEW_SURFACE: {
             LOGV("SET_PREVIEW_SURFACE");
             CHECK_INTERFACE(IMediaRecorder, data, reply);
-            sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder());
+            sp<Surface> surface = Surface::readFromParcel(data);
             reply->writeInt32(setPreviewSurface(surface));
             return NO_ERROR;
         } break;
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 32cdb49..39552b6 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -26,10 +26,7 @@
 
 #include <media/Visualizer.h>
 
-extern "C" {
-#define FLOATING_POINT 1
-#include "fftwrap.h"
-}
+extern void fixed_fft_real(int n, int32_t *v);
 
 namespace android {
 
@@ -47,18 +44,10 @@
         mCaptureCbkUser(NULL)
 {
     initCaptureSize();
-    if (mCaptureSize != 0) {
-        mFftTable = spx_fft_init(mCaptureSize);
-    } else {
-        mFftTable = NULL;
-    }
 }
 
 Visualizer::~Visualizer()
 {
-    if (mFftTable != NULL) {
-        spx_fft_destroy(mFftTable);
-    }
 }
 
 status_t Visualizer::setEnabled(bool enabled)
@@ -163,11 +152,6 @@
     }
     if (status == NO_ERROR) {
         mCaptureSize = size;
-        if (mFftTable != NULL) {
-            spx_fft_destroy(mFftTable);
-        }
-        mFftTable = spx_fft_init(mCaptureSize);
-        LOGV("setCaptureSize size %d mFftTable %p", mCaptureSize, mFftTable);
     }
 
     return status;
@@ -219,19 +203,24 @@
 
 status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
 {
-    if (mFftTable == NULL) {
-        return NO_INIT;
+    int32_t workspace[mCaptureSize >> 1];
+    int32_t nonzero = 0;
+
+    for (uint32_t i = 0; i < mCaptureSize; i += 2) {
+        workspace[i >> 1] = (waveform[i] ^ 0x80) << 23;
+        workspace[i >> 1] |= (waveform[i + 1] ^ 0x80) << 7;
+        nonzero |= workspace[i >> 1];
     }
 
-    float fsrc[mCaptureSize];
-    for (uint32_t i = 0; i < mCaptureSize; i++) {
-        fsrc[i] = (int16_t)(waveform[i] ^ 0x80) << 8;
+    if (nonzero) {
+        fixed_fft_real(mCaptureSize >> 1, workspace);
     }
-    float fdst[mCaptureSize];
-    spx_fft_float(mFftTable, fsrc, fdst);
-    for (uint32_t i = 0; i < mCaptureSize; i++) {
-        fft[i] = (uint8_t)((int32_t)fdst[i] >> 8);
+
+    for (uint32_t i = 0; i < mCaptureSize; i += 2) {
+        fft[i] = workspace[i >> 1] >> 23;
+        fft[i + 1] = workspace[i >> 1] >> 7;
     }
+
     return NO_ERROR;
 }
 
diff --git a/media/libmedia/fixedfft.cpp b/media/libmedia/fixedfft.cpp
new file mode 100644
index 0000000..9cf05ba
--- /dev/null
+++ b/media/libmedia/fixedfft.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+/* A Fixed point implementation of Fast Fourier Transform (FFT). Complex numbers
+ * are represented by 32-bit integers, where higher 16 bits are real part and
+ * lower ones are imaginary part. Few compromises are made between efficiency,
+ * accuracy, and maintainability. To make it fast, arithmetic shifts are used
+ * instead of divisions, and bitwise inverses are used instead of negates. To
+ * keep it small, only radix-2 Cooley-Tukey algorithm is implemented, and only
+ * half of the twiddle factors are stored. Although there are still ways to make
+ * it even faster or smaller, it costs too much on one of the aspects.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#ifdef __ARM_ARCH__
+#include <machine/cpu-features.h>
+#endif
+
+#define LOG_FFT_SIZE 10
+#define MAX_FFT_SIZE (1 << LOG_FFT_SIZE)
+
+static const int32_t twiddle[MAX_FFT_SIZE / 4] = {
+    0x00008000, 0xff378001, 0xfe6e8002, 0xfda58006, 0xfcdc800a, 0xfc13800f,
+    0xfb4a8016, 0xfa81801e, 0xf9b88027, 0xf8ef8032, 0xf827803e, 0xf75e804b,
+    0xf6958059, 0xf5cd8068, 0xf5058079, 0xf43c808b, 0xf374809e, 0xf2ac80b2,
+    0xf1e480c8, 0xf11c80de, 0xf05580f6, 0xef8d8110, 0xeec6812a, 0xedff8146,
+    0xed388163, 0xec718181, 0xebab81a0, 0xeae481c1, 0xea1e81e2, 0xe9588205,
+    0xe892822a, 0xe7cd824f, 0xe7078276, 0xe642829d, 0xe57d82c6, 0xe4b982f1,
+    0xe3f4831c, 0xe3308349, 0xe26d8377, 0xe1a983a6, 0xe0e683d6, 0xe0238407,
+    0xdf61843a, 0xde9e846e, 0xdddc84a3, 0xdd1b84d9, 0xdc598511, 0xdb998549,
+    0xdad88583, 0xda1885be, 0xd95885fa, 0xd8988637, 0xd7d98676, 0xd71b86b6,
+    0xd65c86f6, 0xd59e8738, 0xd4e1877b, 0xd42487c0, 0xd3678805, 0xd2ab884c,
+    0xd1ef8894, 0xd13488dd, 0xd0798927, 0xcfbe8972, 0xcf0489be, 0xce4b8a0c,
+    0xcd928a5a, 0xccd98aaa, 0xcc218afb, 0xcb698b4d, 0xcab28ba0, 0xc9fc8bf5,
+    0xc9468c4a, 0xc8908ca1, 0xc7db8cf8, 0xc7278d51, 0xc6738dab, 0xc5c08e06,
+    0xc50d8e62, 0xc45b8ebf, 0xc3a98f1d, 0xc2f88f7d, 0xc2488fdd, 0xc198903e,
+    0xc0e990a1, 0xc03a9105, 0xbf8c9169, 0xbedf91cf, 0xbe329236, 0xbd86929e,
+    0xbcda9307, 0xbc2f9371, 0xbb8593dc, 0xbadc9448, 0xba3394b5, 0xb98b9523,
+    0xb8e39592, 0xb83c9603, 0xb7969674, 0xb6f196e6, 0xb64c9759, 0xb5a897ce,
+    0xb5059843, 0xb46298b9, 0xb3c09930, 0xb31f99a9, 0xb27f9a22, 0xb1df9a9c,
+    0xb1409b17, 0xb0a29b94, 0xb0059c11, 0xaf689c8f, 0xaecc9d0e, 0xae319d8e,
+    0xad979e0f, 0xacfd9e91, 0xac659f14, 0xabcd9f98, 0xab36a01c, 0xaaa0a0a2,
+    0xaa0aa129, 0xa976a1b0, 0xa8e2a238, 0xa84fa2c2, 0xa7bda34c, 0xa72ca3d7,
+    0xa69ca463, 0xa60ca4f0, 0xa57ea57e, 0xa4f0a60c, 0xa463a69c, 0xa3d7a72c,
+    0xa34ca7bd, 0xa2c2a84f, 0xa238a8e2, 0xa1b0a976, 0xa129aa0a, 0xa0a2aaa0,
+    0xa01cab36, 0x9f98abcd, 0x9f14ac65, 0x9e91acfd, 0x9e0fad97, 0x9d8eae31,
+    0x9d0eaecc, 0x9c8faf68, 0x9c11b005, 0x9b94b0a2, 0x9b17b140, 0x9a9cb1df,
+    0x9a22b27f, 0x99a9b31f, 0x9930b3c0, 0x98b9b462, 0x9843b505, 0x97ceb5a8,
+    0x9759b64c, 0x96e6b6f1, 0x9674b796, 0x9603b83c, 0x9592b8e3, 0x9523b98b,
+    0x94b5ba33, 0x9448badc, 0x93dcbb85, 0x9371bc2f, 0x9307bcda, 0x929ebd86,
+    0x9236be32, 0x91cfbedf, 0x9169bf8c, 0x9105c03a, 0x90a1c0e9, 0x903ec198,
+    0x8fddc248, 0x8f7dc2f8, 0x8f1dc3a9, 0x8ebfc45b, 0x8e62c50d, 0x8e06c5c0,
+    0x8dabc673, 0x8d51c727, 0x8cf8c7db, 0x8ca1c890, 0x8c4ac946, 0x8bf5c9fc,
+    0x8ba0cab2, 0x8b4dcb69, 0x8afbcc21, 0x8aaaccd9, 0x8a5acd92, 0x8a0cce4b,
+    0x89becf04, 0x8972cfbe, 0x8927d079, 0x88ddd134, 0x8894d1ef, 0x884cd2ab,
+    0x8805d367, 0x87c0d424, 0x877bd4e1, 0x8738d59e, 0x86f6d65c, 0x86b6d71b,
+    0x8676d7d9, 0x8637d898, 0x85fad958, 0x85beda18, 0x8583dad8, 0x8549db99,
+    0x8511dc59, 0x84d9dd1b, 0x84a3dddc, 0x846ede9e, 0x843adf61, 0x8407e023,
+    0x83d6e0e6, 0x83a6e1a9, 0x8377e26d, 0x8349e330, 0x831ce3f4, 0x82f1e4b9,
+    0x82c6e57d, 0x829de642, 0x8276e707, 0x824fe7cd, 0x822ae892, 0x8205e958,
+    0x81e2ea1e, 0x81c1eae4, 0x81a0ebab, 0x8181ec71, 0x8163ed38, 0x8146edff,
+    0x812aeec6, 0x8110ef8d, 0x80f6f055, 0x80def11c, 0x80c8f1e4, 0x80b2f2ac,
+    0x809ef374, 0x808bf43c, 0x8079f505, 0x8068f5cd, 0x8059f695, 0x804bf75e,
+    0x803ef827, 0x8032f8ef, 0x8027f9b8, 0x801efa81, 0x8016fb4a, 0x800ffc13,
+    0x800afcdc, 0x8006fda5, 0x8002fe6e, 0x8001ff37,
+};
+
+/* Returns the multiplication of \conj{a} and {b}. */
+static inline int32_t mult(int32_t a, int32_t b)
+{
+#if __ARM_ARCH__ >= 6
+    int32_t t = b;
+    __asm__("smuad  %0, %0, %1"          : "+r" (t) : "r" (a));
+    __asm__("smusdx %0, %0, %1"          : "+r" (b) : "r" (a));
+    __asm__("pkhtb  %0, %0, %1, ASR #16" : "+r" (t) : "r" (b));
+    return t;
+#else
+    return (((a >> 16) * (b >> 16) + (int16_t)a * (int16_t)b) & ~0xFFFF) |
+        ((((a >> 16) * (int16_t)b - (int16_t)a * (b >> 16)) >> 16) & 0xFFFF);
+#endif
+}
+
+static inline int32_t half(int32_t a)
+{
+#if __ARM_ARCH__ >= 6
+    __asm__("shadd16 %0, %0, %1" : "+r" (a) : "r" (0));
+    return a;
+#else
+    return ((a >> 1) & ~0x8000) | (a & 0x8000);
+#endif
+}
+
+void fixed_fft(int n, int32_t *v)
+{
+    int scale = LOG_FFT_SIZE, i, p, r;
+
+    for (r = 0, i = 1; i < n; ++i) {
+        for (p = n; !(p & r); p >>= 1, r ^= p);
+        if (i < r) {
+            int32_t t = v[i];
+            v[i] = v[r];
+            v[r] = t;
+        }
+    }
+
+    for (p = 1; p < n; p <<= 1) {
+        --scale;
+
+        for (i = 0; i < n; i += p << 1) {
+            int32_t x = half(v[i]);
+            int32_t y = half(v[i + p]);
+            v[i] = x + y;
+            v[i + p] = x - y;
+        }
+
+        for (r = 1; r < p; ++r) {
+            int32_t w = MAX_FFT_SIZE / 4 - (r << scale);
+            i = w >> 31;
+            w = twiddle[(w ^ i) - i] ^ (i << 16);
+            for (i = r; i < n; i += p << 1) {
+                int32_t x = half(v[i]);
+                int32_t y = mult(w, v[i + p]);
+                v[i] = x - y;
+                v[i + p] = x + y;
+            }
+        }
+    }
+}
+
+void fixed_fft_real(int n, int32_t *v)
+{
+    int scale = LOG_FFT_SIZE, m = n >> 1, i;
+
+    fixed_fft(n, v);
+    for (i = 1; i <= n; i <<= 1, --scale);
+    v[0] = mult(~v[0], 0x80008000);
+    v[m] = half(v[m]);
+
+    for (i = 1; i < n >> 1; ++i) {
+        int32_t x = half(v[i]);
+        int32_t z = half(v[n - i]);
+        int32_t y = z - (x ^ 0xFFFF);
+        x = half(x + (z ^ 0xFFFF));
+        y = mult(y, twiddle[i << scale]);
+        v[i] = x - y;
+        v[n - i] = (x + y) ^ 0xFFFF;
+    }
+}
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index f3229c0..32b20c7 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -206,10 +206,15 @@
     LOGV("setVideoSurface");
     Mutex::Autolock _l(mLock);
     if (mPlayer == 0) return NO_INIT;
-    if (surface != NULL)
-        return  mPlayer->setVideoSurface(surface->getISurface());
-    else
-        return  mPlayer->setVideoSurface(NULL);
+
+    status_t err = mPlayer->setVideoISurface(
+            surface == NULL ? NULL : surface->getISurface());
+
+    if (err != OK) {
+        return err;
+    }
+
+    return mPlayer->setVideoSurface(surface);
 }
 
 // must call with lock held
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 9d53c257e..7f25359 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -65,7 +65,7 @@
         return INVALID_OPERATION;
     }
 
-    status_t ret = mMediaRecorder->setPreviewSurface(surface->getISurface());
+    status_t ret = mMediaRecorder->setPreviewSurface(surface);
     if (OK != ret) {
         LOGV("setPreviewSurface failed: %d", ret);
         mCurrentState = MEDIA_RECORDER_ERROR;
@@ -643,4 +643,3 @@
 }
 
 }; // namespace android
-
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index b5972e7..d9b0c69 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -889,7 +889,15 @@
     return mStatus;
 }
 
-status_t MediaPlayerService::Client::setVideoSurface(const sp<ISurface>& surface)
+status_t MediaPlayerService::Client::setVideoISurface(const sp<ISurface>& surface)
+{
+    LOGV("[%d] setVideoISurface(%p)", mConnId, surface.get());
+    sp<MediaPlayerBase> p = getPlayer();
+    if (p == 0) return UNKNOWN_ERROR;
+    return p->setVideoISurface(surface);
+}
+
+status_t MediaPlayerService::Client::setVideoSurface(const sp<Surface>& surface)
 {
     LOGV("[%d] setVideoSurface(%p)", mConnId, surface.get());
     sp<MediaPlayerBase> p = getPlayer();
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index a967ee2..deb458c 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -204,7 +204,8 @@
 
         // IMediaPlayer interface
         virtual void            disconnect();
-        virtual status_t        setVideoSurface(const sp<ISurface>& surface);
+        virtual status_t        setVideoISurface(const sp<ISurface>& surface);
+        virtual status_t        setVideoSurface(const sp<Surface>& surface);
         virtual status_t        prepareAsync();
         virtual status_t        start();
         virtual status_t        stop();
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 73862c3..7e05043 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -70,7 +70,7 @@
     return mRecorder->setCamera(camera);
 }
 
-status_t MediaRecorderClient::setPreviewSurface(const sp<ISurface>& surface)
+status_t MediaRecorderClient::setPreviewSurface(const sp<Surface>& surface)
 {
     LOGV("setPreviewSurface");
     Mutex::Autolock lock(mLock);
@@ -337,4 +337,3 @@
 }
 
 }; // namespace android
-
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index 1d1913d..6c17217 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -29,7 +29,7 @@
 {
 public:
     virtual     status_t        setCamera(const sp<ICamera>& camera);
-    virtual     status_t        setPreviewSurface(const sp<ISurface>& surface);
+    virtual     status_t        setPreviewSurface(const sp<Surface>& surface);
     virtual     status_t        setVideoSource(int vs);
     virtual     status_t        setAudioSource(int as);
     virtual     status_t        setOutputFormat(int of);
@@ -66,4 +66,3 @@
 }; // namespace android
 
 #endif // ANDROID_MEDIARECORDERCLIENT_H
-
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index 4a60ece..06e4b70 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -35,7 +35,8 @@
             const char* path, const KeyedVector<String8, String8> *headers);
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
-    virtual status_t    setVideoSurface(const sp<ISurface>& surface) { return UNKNOWN_ERROR; }
+    virtual status_t    setVideoISurface(const sp<ISurface>& surface) { return UNKNOWN_ERROR; }
+    virtual status_t    setVideoSurface(const sp<Surface>& surface) { return UNKNOWN_ERROR; }
     virtual status_t    prepare();
     virtual status_t    prepareAsync();
     virtual status_t    start();
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index 2c96d6d..6b9bf85 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -44,13 +44,20 @@
     return mPlayer->setDataSource(dup(fd), offset, length);
 }
 
-status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) {
-    LOGV("setVideoSurface");
+status_t StagefrightPlayer::setVideoISurface(const sp<ISurface> &surface) {
+    LOGV("setVideoISurface");
 
     mPlayer->setISurface(surface);
     return OK;
 }
 
+status_t StagefrightPlayer::setVideoSurface(const sp<Surface> &surface) {
+    LOGV("setVideoSurface");
+
+    mPlayer->setSurface(surface);
+    return OK;
+}
+
 status_t StagefrightPlayer::prepare() {
     return mPlayer->prepare();
 }
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index 781eb44..dd37102 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -35,7 +35,8 @@
             const char *url, const KeyedVector<String8, String8> *headers);
 
     virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
-    virtual status_t setVideoSurface(const sp<ISurface> &surface);
+    virtual status_t setVideoISurface(const sp<ISurface> &surface);
+    virtual status_t setVideoSurface(const sp<Surface> &surface);
     virtual status_t prepare();
     virtual status_t prepareAsync();
     virtual status_t start();
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index a616aae..a24e56c 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -35,7 +35,7 @@
 #include <camera/ICamera.h>
 #include <camera/Camera.h>
 #include <camera/CameraParameters.h>
-#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
 #include <utils/Errors.h>
 #include <sys/types.h>
 #include <ctype.h>
@@ -201,7 +201,7 @@
     return OK;
 }
 
-status_t StagefrightRecorder::setPreviewSurface(const sp<ISurface> &surface) {
+status_t StagefrightRecorder::setPreviewSurface(const sp<Surface> &surface) {
     LOGV("setPreviewSurface: %p", surface.get());
     mPreviewSurface = surface;
 
@@ -342,10 +342,14 @@
 
 status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) {
     LOGV("setParamMaxFileDurationUs: %lld us", timeUs);
-    if (timeUs <= 100000LL) {  // XXX: 100 milli-seconds
+    if (timeUs <= 0) {
+        LOGW("Max file duration is not positive: %lld us. Disabling duration limit.", timeUs);
+        timeUs = 0; // Disable the duration limit for zero or negative values.
+    } else if (timeUs <= 100000LL) {  // XXX: 100 milli-seconds
         LOGE("Max file duration is too short: %lld us", timeUs);
         return BAD_VALUE;
     }
+
     mMaxFileDurationUs = timeUs;
     return OK;
 }
@@ -1141,8 +1145,9 @@
 
 status_t StagefrightRecorder::stop() {
     LOGV("stop");
+    status_t err = OK;
     if (mWriter != NULL) {
-        mWriter->stop();
+        err = mWriter->stop();
         mWriter.clear();
     }
 
@@ -1164,7 +1169,7 @@
         mOutputFd = -1;
     }
 
-    return OK;
+    return err;
 }
 
 status_t StagefrightRecorder::close() {
@@ -1234,11 +1239,19 @@
     return OK;
 }
 
-status_t StagefrightRecorder::dump(int fd, const Vector<String16>& args) const {
+status_t StagefrightRecorder::dump(
+        int fd, const Vector<String16>& args) const {
+    LOGV("dump");
     const size_t SIZE = 256;
     char buffer[SIZE];
     String8 result;
-    snprintf(buffer, SIZE, "   Recorder: %p", this);
+    if (mWriter != 0) {
+        mWriter->dump(fd, args);
+    } else {
+        snprintf(buffer, SIZE, "   No file writer\n");
+        result.append(buffer);
+    }
+    snprintf(buffer, SIZE, "   Recorder: %p\n", this);
     snprintf(buffer, SIZE, "   Output file (fd %d):\n", mOutputFd);
     result.append(buffer);
     snprintf(buffer, SIZE, "     File format: %d\n", mOutputFormat);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index a8be27d..628e19b 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -42,7 +42,7 @@
     virtual status_t setVideoSize(int width, int height);
     virtual status_t setVideoFrameRate(int frames_per_second);
     virtual status_t setCamera(const sp<ICamera>& camera);
-    virtual status_t setPreviewSurface(const sp<ISurface>& surface);
+    virtual status_t setPreviewSurface(const sp<Surface>& surface);
     virtual status_t setOutputFile(const char *path);
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
     virtual status_t setParameters(const String8& params);
@@ -63,7 +63,7 @@
     };
 
     sp<Camera> mCamera;
-    sp<ISurface> mPreviewSurface;
+    sp<Surface> mPreviewSurface;
     sp<IMediaRecorderClient> mListener;
     sp<MediaWriter> mWriter;
     sp<AudioSource> mAudioSourceNode;
@@ -144,4 +144,3 @@
 }  // namespace android
 
 #endif  // STAGEFRIGHT_RECORDER_H_
-
diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h
index 6e6c3cd..5eaf592 100644
--- a/media/libmediaplayerservice/TestPlayerStub.h
+++ b/media/libmediaplayerservice/TestPlayerStub.h
@@ -75,7 +75,10 @@
 
 
     // All the methods below wrap the mPlayer instance.
-    virtual status_t setVideoSurface(const android::sp<android::ISurface>& s)  {
+    virtual status_t setVideoISurface(const android::sp<android::ISurface>& s)  {
+        return mPlayer->setVideoISurface(s);
+    }
+    virtual status_t setVideoSurface(const android::sp<android::Surface>& s)  {
         return mPlayer->setVideoSurface(s);
     }
     virtual status_t prepare() {return mPlayer->prepare();}
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 70af2da..1b05528 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -87,7 +87,7 @@
       mInitCheck(NO_INIT) {
     String8 mimeType;
     float confidence;
-    if (!SniffAMR(mDataSource, &mimeType, &confidence)) {
+    if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) {
         return;
     }
 
@@ -276,7 +276,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffAMR(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     char header[9];
 
     if (source->readAt(0, header, sizeof(header)) != sizeof(header)) {
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index c71743e..71d48b3 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -136,16 +136,17 @@
     return OK;
 }
 
-void AMRWriter::pause() {
+status_t AMRWriter::pause() {
     if (!mStarted) {
-        return;
+        return OK;
     }
     mPaused = true;
+    return OK;
 }
 
-void AMRWriter::stop() {
+status_t AMRWriter::stop() {
     if (!mStarted) {
-        return;
+        return OK;
     }
 
     mDone = true;
@@ -153,9 +154,17 @@
     void *dummy;
     pthread_join(mThread, &dummy);
 
-    mSource->stop();
+    status_t err = (status_t) dummy;
+    {
+        status_t status = mSource->stop();
+        if (err == OK &&
+            (status != OK && status != ERROR_END_OF_STREAM)) {
+            err = status;
+        }
+    }
 
     mStarted = false;
+    return err;
 }
 
 bool AMRWriter::exceedsFileSizeLimit() {
@@ -174,21 +183,20 @@
 
 // static
 void *AMRWriter::ThreadWrapper(void *me) {
-    static_cast<AMRWriter *>(me)->threadFunc();
-
-    return NULL;
+    return (void *) static_cast<AMRWriter *>(me)->threadFunc();
 }
 
-void AMRWriter::threadFunc() {
+status_t AMRWriter::threadFunc() {
     mEstimatedDurationUs = 0;
     mEstimatedSizeBytes = 0;
     bool stoppedPrematurely = true;
     int64_t previousPausedDurationUs = 0;
     int64_t maxTimestampUs = 0;
+    status_t err = OK;
 
     while (!mDone) {
         MediaBuffer *buffer;
-        status_t err = mSource->read(&buffer);
+        err = mSource->read(&buffer);
 
         if (err != OK) {
             break;
@@ -260,6 +268,10 @@
     fclose(mFile);
     mFile = NULL;
     mReachedEOS = true;
+    if (err == ERROR_END_OF_STREAM) {
+        return OK;
+    }
+    return err;
 }
 
 bool AMRWriter::reachedEOS() {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 404762f..b5a6327 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -40,6 +40,7 @@
         TimedEventQueue.cpp               \
         Utils.cpp                         \
         WAVExtractor.cpp                  \
+        avc_utils.cpp                     \
         string.cpp
 
 LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index f2653cf..1ccfa03 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -44,7 +44,7 @@
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXCodec.h>
 
-#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
 
 #include <media/stagefright/foundation/ALooper.h>
 
@@ -97,13 +97,14 @@
             bool previewOnly,
             const char *componentName,
             OMX_COLOR_FORMATTYPE colorFormat,
-            const sp<ISurface> &surface,
+            const sp<ISurface> &isurface,
+            const sp<Surface> &surface,
             size_t displayWidth, size_t displayHeight,
             size_t decodedWidth, size_t decodedHeight)
         : mTarget(NULL),
           mLibHandle(NULL) {
             init(previewOnly, componentName,
-                 colorFormat, surface, displayWidth,
+                 colorFormat, isurface, surface, displayWidth,
                  displayHeight, decodedWidth, decodedHeight);
     }
 
@@ -135,7 +136,8 @@
             bool previewOnly,
             const char *componentName,
             OMX_COLOR_FORMATTYPE colorFormat,
-            const sp<ISurface> &surface,
+            const sp<ISurface> &isurface,
+            const sp<Surface> &surface,
             size_t displayWidth, size_t displayHeight,
             size_t decodedWidth, size_t decodedHeight);
 
@@ -147,7 +149,8 @@
         bool previewOnly,
         const char *componentName,
         OMX_COLOR_FORMATTYPE colorFormat,
-        const sp<ISurface> &surface,
+        const sp<ISurface> &isurface,
+        const sp<Surface> &surface,
         size_t displayWidth, size_t displayHeight,
         size_t decodedWidth, size_t decodedHeight) {
     if (!previewOnly) {
@@ -173,7 +176,7 @@
 
             if (func) {
                 mTarget =
-                    (*func)(surface, componentName, colorFormat,
+                    (*func)(isurface, componentName, colorFormat,
                         displayWidth, displayHeight,
                         decodedWidth, decodedHeight);
             }
@@ -196,6 +199,7 @@
       mExtractorFlags(0),
       mLastVideoBuffer(NULL),
       mVideoBuffer(NULL),
+      mRTSPTimeOffset(0),
       mSuspensionState(NULL) {
     CHECK_EQ(mClient.connect(), OK);
 
@@ -393,7 +397,11 @@
         mVideoBuffer = NULL;
     }
 
-    mRTSPController.clear();
+    if (mRTSPController != NULL) {
+        mRTSPController->disconnect();
+        mRTSPController.clear();
+    }
+
     mRTPPusher.clear();
     mRTCPPusher.clear();
     mRTPSession.clear();
@@ -453,27 +461,34 @@
         return;
     }
 
+    bool eos;
+    size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
+
     size_t lowWatermark = 400000;
     size_t highWatermark = 1000000;
 
-    off_t size;
-    if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
-        int64_t bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
+    if (eos) {
+        notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+    } else {
+        off_t size;
+        if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
+            int64_t bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
 
-        size_t cachedSize = mCachedSource->cachedSize();
-        int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
+            size_t cachedSize = mCachedSource->cachedSize();
+            int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
 
-        double percentage = (double)cachedDurationUs / mDurationUs;
+            int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
+            if (percentage > 100) {
+                percentage = 100;
+            }
 
-        notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage * 100.0);
+            notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
 
-        lowWatermark = 2 * bitrate / 8;  // 2 secs
-        highWatermark = 10 * bitrate / 8;  // 10 secs
+            lowWatermark = 2 * bitrate / 8;  // 2 secs
+            highWatermark = 10 * bitrate / 8;  // 10 secs
+        }
     }
 
-    bool eos;
-    size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
-
     if ((mFlags & PLAYING) && !eos && (cachedDataRemaining < lowWatermark)) {
         LOGI("cache is running low (< %d) , pausing.", lowWatermark);
         mFlags |= CACHE_UNDERRUN;
@@ -619,8 +634,18 @@
     return OK;
 }
 
+void AwesomePlayer::notifyVideoSize_l() {
+    sp<MetaData> meta = mVideoSource->getFormat();
+
+    int32_t decodedWidth, decodedHeight;
+    CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
+    CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
+
+    notifyListener_l(MEDIA_SET_VIDEO_SIZE, decodedWidth, decodedHeight);
+}
+
 void AwesomePlayer::initRenderer_l() {
-    if (mISurface != NULL) {
+    if (mSurface != NULL || mISurface != NULL) {
         sp<MetaData> meta = mVideoSource->getFormat();
 
         int32_t format;
@@ -637,7 +662,18 @@
         // before creating a new one.
         IPCThreadState::self()->flushCommands();
 
-        if (!strncmp("OMX.", component, 4)) {
+        if (mSurface != NULL) {
+            // Other decoders are instantiated locally and as a consequence
+            // allocate their buffers in local address space.
+            mVideoRenderer = new AwesomeLocalRenderer(
+                false,  // previewOnly
+                component,
+                (OMX_COLOR_FORMATTYPE)format,
+                mISurface,
+                mSurface,
+                mVideoWidth, mVideoHeight,
+                decodedWidth, decodedHeight);
+        } else {
             // Our OMX codecs allocate buffers on the media_server side
             // therefore they require a remote IOMXRenderer that knows how
             // to display them.
@@ -647,16 +683,6 @@
                         (OMX_COLOR_FORMATTYPE)format,
                         decodedWidth, decodedHeight,
                         mVideoWidth, mVideoHeight));
-        } else {
-            // Other decoders are instantiated locally and as a consequence
-            // allocate their buffers in local address space.
-            mVideoRenderer = new AwesomeLocalRenderer(
-                false,  // previewOnly
-                component,
-                (OMX_COLOR_FORMATTYPE)format,
-                mISurface,
-                mVideoWidth, mVideoHeight,
-                decodedWidth, decodedHeight);
         }
     }
 }
@@ -695,6 +721,12 @@
     mISurface = isurface;
 }
 
+void AwesomePlayer::setSurface(const sp<Surface> &surface) {
+    Mutex::Autolock autoLock(mLock);
+
+    mSurface = surface;
+}
+
 void AwesomePlayer::setAudioSink(
         const sp<MediaPlayerBase::AudioSink> &audioSink) {
     Mutex::Autolock autoLock(mLock);
@@ -738,6 +770,10 @@
         *positionUs = 0;
     }
 
+    if (mRTSPController != NULL) {
+        *positionUs += mRTSPTimeOffset;
+    }
+
     return OK;
 }
 
@@ -753,6 +789,17 @@
 }
 
 status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
+    if (mRTSPController != NULL) {
+        pause_l();
+        mRTSPController->seek(timeUs);
+        play_l();
+
+        notifyListener_l(MEDIA_SEEK_COMPLETE);
+        mSeekNotificationSent = true;
+        mRTSPTimeOffset = timeUs;
+        return OK;
+    }
+
     if (mFlags & CACHE_UNDERRUN) {
         mFlags &= ~CACHE_UNDERRUN;
         play_l();
@@ -937,6 +984,8 @@
                 if (err == INFO_FORMAT_CHANGED) {
                     LOGV("VideoSource signalled format change.");
 
+                    notifyVideoSize_l();
+
                     if (mVideoRenderer != NULL) {
                         mVideoRendererIsPreview = false;
                         initRenderer_l();
@@ -1422,10 +1471,10 @@
     Mutex::Autolock autoLock(mLock);
 
     if (mIsAsyncPrepare) {
-        if (mVideoWidth < 0 || mVideoHeight < 0) {
+        if (mVideoSource == NULL) {
             notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
         } else {
-            notifyListener_l(MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight);
+            notifyVideoSize_l();
         }
 
         notifyListener_l(MEDIA_PREPARED);
@@ -1540,13 +1589,14 @@
 
     mFlags = state->mFlags & (LOOPING | AT_EOS);
 
-    if (state->mLastVideoFrame && mISurface != NULL) {
+    if (state->mLastVideoFrame && (mSurface != NULL || mISurface != NULL)) {
         mVideoRenderer =
             new AwesomeLocalRenderer(
                     true,  // previewOnly
                     "",
                     (OMX_COLOR_FORMATTYPE)state->mColorFormat,
                     mISurface,
+                    mSurface,
                     state->mVideoWidth,
                     state->mVideoHeight,
                     state->mDecodedWidth,
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 90a596c..49eac62 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -25,6 +25,7 @@
 
 #include "matroska/MatroskaExtractor.h"
 
+#include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/MediaErrors.h>
@@ -56,19 +57,23 @@
 Mutex DataSource::gSnifferMutex;
 List<DataSource::SnifferFunc> DataSource::gSniffers;
 
-bool DataSource::sniff(String8 *mimeType, float *confidence) {
+bool DataSource::sniff(
+        String8 *mimeType, float *confidence, sp<AMessage> *meta) {
     *mimeType = "";
     *confidence = 0.0f;
+    meta->clear();
 
     Mutex::Autolock autoLock(gSnifferMutex);
     for (List<SnifferFunc>::iterator it = gSniffers.begin();
          it != gSniffers.end(); ++it) {
         String8 newMimeType;
         float newConfidence;
-        if ((*it)(this, &newMimeType, &newConfidence)) {
+        sp<AMessage> newMeta;
+        if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {
             if (newConfidence > *confidence) {
                 *mimeType = newMimeType;
                 *confidence = newConfidence;
+                *meta = newMeta;
             }
         }
     }
@@ -92,13 +97,13 @@
 
 // static
 void DataSource::RegisterDefaultSniffers() {
-    RegisterSniffer(SniffMP3);
     RegisterSniffer(SniffMPEG4);
-    RegisterSniffer(SniffAMR);
-    RegisterSniffer(SniffWAV);
-    RegisterSniffer(SniffOgg);
     RegisterSniffer(SniffMatroska);
+    RegisterSniffer(SniffOgg);
+    RegisterSniffer(SniffWAV);
+    RegisterSniffer(SniffAMR);
     RegisterSniffer(SniffMPEG2TS);
+    RegisterSniffer(SniffMP3);
 }
 
 // static
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 4058fbc..2e36968 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -22,6 +22,7 @@
 
 #include "include/ID3.h"
 
+#include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
@@ -456,15 +457,31 @@
     MP3Source &operator=(const MP3Source &);
 };
 
-MP3Extractor::MP3Extractor(const sp<DataSource> &source)
+MP3Extractor::MP3Extractor(
+        const sp<DataSource> &source, const sp<AMessage> &meta)
     : mDataSource(source),
       mFirstFramePos(-1),
       mFixedHeader(0),
       mByteNumber(0) {
     off_t pos = 0;
     uint32_t header;
-    bool success = Resync(mDataSource, 0, &pos, &header);
-    CHECK(success);
+    bool success;
+
+    int64_t meta_offset;
+    uint32_t meta_header;
+    if (meta != NULL
+            && meta->findInt64("offset", &meta_offset)
+            && meta->findInt32("header", (int32_t *)&meta_header)) {
+        // The sniffer has already done all the hard work for us, simply
+        // accept its judgement.
+        pos = (off_t)meta_offset;
+        header = meta_header;
+
+        success = true;
+    } else {
+        success = Resync(mDataSource, 0, &pos, &header);
+        CHECK(success);
+    }
 
     if (success) {
         mFirstFramePos = pos;
@@ -759,15 +776,20 @@
 }
 
 bool SniffMP3(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType,
+        float *confidence, sp<AMessage> *meta) {
     off_t pos = 0;
     uint32_t header;
     if (!Resync(source, 0, &pos, &header)) {
         return false;
     }
 
+    *meta = new AMessage;
+    (*meta)->setInt64("offset", pos);
+    (*meta)->setInt32("header", header);
+
     *mimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
-    *confidence = 0.3f;
+    *confidence = 0.2f;
 
     return true;
 }
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 12a1e6e..ba90407 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1738,7 +1738,7 @@
         || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)
         || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) {
         *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
-        *confidence = 0.1;
+        *confidence = 0.4;
 
         return true;
     }
@@ -1805,13 +1805,14 @@
     }
 
     *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
-    *confidence = 0.3f;
+    *confidence = 0.4f;
 
     return true;
 }
 
 bool SniffMPEG4(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     if (BetterSniffMPEG4(source, mimeType, confidence)) {
         return true;
     }
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f52ec1a..a15b84e 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -48,8 +48,8 @@
     ~Track();
 
     status_t start(MetaData *params);
-    void stop();
-    void pause();
+    status_t stop();
+    status_t pause();
     bool reachedEOS();
 
     int64_t getDurationUs() const;
@@ -60,6 +60,7 @@
     bool isAudio() const { return mIsAudio; }
     bool isMPEG4() const { return mIsMPEG4; }
     void addChunkOffset(off_t offset) { mChunkOffsets.push_back(offset); }
+    status_t dump(int fd, const Vector<String16>& args) const;
 
 private:
     MPEG4Writer *mOwner;
@@ -144,7 +145,7 @@
     int64_t mTrackEveryTimeDurationUs;
 
     static void *ThreadWrapper(void *me);
-    void threadEntry();
+    status_t threadEntry();
 
     const uint8_t *parseParamSet(
         const uint8_t *data, size_t length, int type, size_t *paramSetLen);
@@ -174,6 +175,9 @@
     // value, the user-supplied time scale will be used.
     void setTimeScale();
 
+    // Simple validation on the codec specific data
+    status_t checkCodecSpecificData() const;
+
     Track(const Track &);
     Track &operator=(const Track &);
 };
@@ -214,6 +218,37 @@
     mTracks.clear();
 }
 
+status_t MPEG4Writer::dump(
+        int fd, const Vector<String16>& args) {
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+    snprintf(buffer, SIZE, "   MPEG4Writer %p\n", this);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "     mStarted: %s\n", mStarted? "true": "false");
+    result.append(buffer);
+    ::write(fd, result.string(), result.size());
+    for (List<Track *>::iterator it = mTracks.begin();
+         it != mTracks.end(); ++it) {
+        (*it)->dump(fd, args);
+    }
+    return OK;
+}
+
+status_t MPEG4Writer::Track::dump(
+        int fd, const Vector<String16>& args) const {
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+    snprintf(buffer, SIZE, "     %s track\n", mIsAudio? "Audio": "Video");
+    result.append(buffer);
+    snprintf(buffer, SIZE, "       reached EOS: %s\n",
+            mReachedEOS? "true": "false");
+    result.append(buffer);
+    ::write(fd, result.string(), result.size());
+    return OK;
+}
+
 status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
     Track *track = new Track(this, source);
     mTracks.push_back(track);
@@ -378,15 +413,20 @@
     return OK;
 }
 
-void MPEG4Writer::pause() {
+status_t MPEG4Writer::pause() {
     if (mFile == NULL) {
-        return;
+        return OK;
     }
     mPaused = true;
+    status_t err = OK;
     for (List<Track *>::iterator it = mTracks.begin();
          it != mTracks.end(); ++it) {
-        (*it)->pause();
+        status_t status = (*it)->pause();
+        if (status != OK) {
+            err = status;
+        }
     }
+    return err;
 }
 
 void MPEG4Writer::stopWriterThread() {
@@ -403,15 +443,19 @@
     pthread_join(mThread, &dummy);
 }
 
-void MPEG4Writer::stop() {
+status_t MPEG4Writer::stop() {
     if (mFile == NULL) {
-        return;
+        return OK;
     }
 
+    status_t err = OK;
     int64_t maxDurationUs = 0;
     for (List<Track *>::iterator it = mTracks.begin();
          it != mTracks.end(); ++it) {
-        (*it)->stop();
+        status_t status = (*it)->stop();
+        if (err == OK && status != OK) {
+            err = status;
+        }
 
         int64_t durationUs = (*it)->getDurationUs();
         if (durationUs > maxDurationUs) {
@@ -421,6 +465,15 @@
 
     stopWriterThread();
 
+    // Do not write out movie header on error.
+    if (err != OK) {
+        fflush(mFile);
+        fclose(mFile);
+        mFile = NULL;
+        mStarted = false;
+        return err;
+    }
+
     // Fix up the size of the 'mdat' chunk.
     if (mUse32BitOffset) {
         fseeko(mFile, mMdatOffset, SEEK_SET);
@@ -508,6 +561,7 @@
     fclose(mFile);
     mFile = NULL;
     mStarted = false;
+    return err;
 }
 
 status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
@@ -1030,13 +1084,14 @@
     return OK;
 }
 
-void MPEG4Writer::Track::pause() {
+status_t MPEG4Writer::Track::pause() {
     mPaused = true;
+    return OK;
 }
 
-void MPEG4Writer::Track::stop() {
+status_t MPEG4Writer::Track::stop() {
     if (mDone) {
-        return;
+        return OK;
     }
 
     mDone = true;
@@ -1044,7 +1099,16 @@
     void *dummy;
     pthread_join(mThread, &dummy);
 
-    mSource->stop();
+    status_t err = (status_t) dummy;
+
+    {
+        status_t status = mSource->stop();
+        if (err == OK && status != OK && status != ERROR_END_OF_STREAM) {
+            err = status;
+        }
+    }
+
+    return err;
 }
 
 bool MPEG4Writer::Track::reachedEOS() {
@@ -1055,9 +1119,8 @@
 void *MPEG4Writer::Track::ThreadWrapper(void *me) {
     Track *track = static_cast<Track *>(me);
 
-    track->threadEntry();
-
-    return NULL;
+    status_t err = track->threadEntry();
+    return (void *) err;
 }
 
 #include <ctype.h>
@@ -1352,7 +1415,7 @@
     return false;
 }
 
-void MPEG4Writer::Track::threadEntry() {
+status_t MPEG4Writer::Track::threadEntry() {
     int32_t count = 0;
     const int64_t interleaveDurationUs = mOwner->interleaveDuration();
     int64_t chunkTimestampUs = 0;
@@ -1493,10 +1556,24 @@
         CHECK(timestampUs >= 0);
         if (mNumSamples > 1) {
             if (timestampUs <= lastTimestampUs) {
-                LOGW("Drop a frame, since it arrives too late!");
+                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
+
+                // The idea here is to avoid having two or more samples with the
+                // same timestamp in the output file.
+                if (mTimeScale >= 1000000LL) {
+                    timestampUs = lastTimestampUs + 1;
+                } else {
+                    timestampUs = lastTimestampUs + (1000000LL + (mTimeScale >> 1)) / mTimeScale;
+                }
+#endif
             }
         }
 
@@ -1595,7 +1672,9 @@
     }
 
     if (mSampleSizes.empty()) {
-        err = UNKNOWN_ERROR;
+        err = ERROR_MALFORMED;
+    } else if (OK != checkCodecSpecificData()) {
+        err = ERROR_MALFORMED;
     }
     mOwner->trackProgressStatus(this, -1, err);
 
@@ -1626,6 +1705,10 @@
             count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
 
     logStatisticalData(mIsAudio);
+    if (err == ERROR_END_OF_STREAM) {
+        return OK;
+    }
+    return err;
 }
 
 void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
@@ -1801,6 +1884,27 @@
     return mEstimatedTrackSizeBytes;
 }
 
+status_t MPEG4Writer::Track::checkCodecSpecificData() const {
+    const char *mime;
+    CHECK(mMeta->findCString(kKeyMIMEType, &mime));
+    if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) ||
+        !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
+        !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+        if (!mCodecSpecificData ||
+            mCodecSpecificDataSize <= 0) {
+            // Missing codec specific data
+            return ERROR_MALFORMED;
+        }
+    } else {
+        if (mCodecSpecificData ||
+            mCodecSpecificDataSize > 0) {
+            // Unexepected codec specific data found
+            return ERROR_MALFORMED;
+        }
+    }
+    return OK;
+}
+
 void MPEG4Writer::Track::writeTrackHeader(
         int32_t trackID, bool use32BitOffset) {
     const char *mime;
@@ -1973,7 +2077,6 @@
                   int32_t samplerate;
                   bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
                   CHECK(success);
-
                   mOwner->writeInt32(samplerate << 16);
                   if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
                     mOwner->beginBox("esds");
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 56e6136..9bc94de 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -27,6 +27,7 @@
 
 #include "matroska/MatroskaExtractor.h"
 
+#include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaExtractor.h>
@@ -46,10 +47,12 @@
 // static
 sp<MediaExtractor> MediaExtractor::Create(
         const sp<DataSource> &source, const char *mime) {
+    sp<AMessage> meta;
+
     String8 tmp;
     if (mime == NULL) {
         float confidence;
-        if (!source->sniff(&tmp, &confidence)) {
+        if (!source->sniff(&tmp, &confidence, &meta)) {
             LOGV("FAILED to autodetect media content.");
 
             return NULL;
@@ -64,7 +67,7 @@
             || !strcasecmp(mime, "audio/mp4")) {
         return new MPEG4Extractor(source);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
-        return new MP3Extractor(source);
+        return new MP3Extractor(source, meta);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
         return new AMRExtractor(source);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4741b1d..37def3b 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -146,29 +146,36 @@
 
 static const CodecInfo kDecoderInfo[] = {
     { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
+//    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.Nvidia.mp3.decoder" },
 //    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
     { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" },
 //    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.PV.mp3dec" },
 //    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },
+//    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amr.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" },
 //    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrdec" },
+//    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amrwb.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" },
 //    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.PV.amrdec" },
+//    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.Nvidia.aac.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
 //    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
     { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" },
     { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" },
+//    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.decode" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
 //    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" },
+//    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.decode" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
 //    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" },
+    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.decode" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
@@ -199,6 +206,7 @@
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.encoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" },
+    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.encoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "AVCEncoder" },
 //    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" },
 };
@@ -337,6 +345,13 @@
 uint32_t OMXCodec::getComponentQuirks(const char *componentName) {
     uint32_t quirks = 0;
 
+    if (!strcmp(componentName, "OMX.Nvidia.amr.decoder") ||
+         !strcmp(componentName, "OMX.Nvidia.amrwb.decoder") ||
+         !strcmp(componentName, "OMX.Nvidia.aac.decoder") ||
+         !strcmp(componentName, "OMX.Nvidia.mp3.decoder")) {
+        quirks |= kDecoderLiesAboutNumberOfChannels;
+    }
+
     if (!strcmp(componentName, "OMX.PV.avcdec")) {
         quirks |= kWantsNALFragments;
     }
@@ -854,6 +869,10 @@
     OMX_COLOR_FORMATTYPE colorFormat;
     CHECK_EQ(OK, findTargetColorFormat(meta, &colorFormat));
 
+    if (!strcasecmp("OMX.Nvidia.h264.encoder", mComponentName)) {
+        colorFormat = OMX_COLOR_FormatYUV420Planar;
+    }
+
     status_t err;
     OMX_PARAM_PORTDEFINITIONTYPE def;
     OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
@@ -1193,6 +1212,10 @@
     h264type.bMBAFF = OMX_FALSE;
     h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
 
+    if (!strcasecmp("OMX.Nvidia.h264.encoder", mComponentName)) {
+        h264type.eLevel = OMX_VIDEO_AVCLevelMax;
+    }
+
     err = mOMX->setParameter(
             mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
     CHECK_EQ(err, OK);
@@ -2165,6 +2188,7 @@
     CHECK_EQ(mPortStatus[portIndex], ENABLED);
     mPortStatus[portIndex] = DISABLING;
 
+    CODEC_LOGV("sending OMX_CommandPortDisable(%ld)", portIndex);
     status_t err =
         mOMX->sendCommand(mNode, OMX_CommandPortDisable, portIndex);
     CHECK_EQ(err, OK);
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 9630092..2c1311a 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -804,7 +804,8 @@
 }
 
 bool SniffOgg(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     char tmp[4];
     if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
         return false;
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 8d820c0..57c1075 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -404,7 +404,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffWAV(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     char header[12];
     if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
         return false;
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
new file mode 100644
index 0000000..511ae12
--- /dev/null
+++ b/media/libstagefright/avc_utils.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/avc_utils.h"
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+static unsigned parseUE(ABitReader *br) {
+    unsigned numZeroes = 0;
+    while (br->getBits(1) == 0) {
+        ++numZeroes;
+    }
+
+    unsigned x = br->getBits(numZeroes);
+
+    return x + (1u << numZeroes) - 1;
+}
+
+// Determine video dimensions from the sequence parameterset.
+void FindAVCDimensions(
+        const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height) {
+    ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
+
+    unsigned profile_idc = br.getBits(8);
+    br.skipBits(16);
+    parseUE(&br);  // seq_parameter_set_id
+
+    if (profile_idc == 100 || profile_idc == 110
+            || profile_idc == 122 || profile_idc == 244
+            || profile_idc == 44 || profile_idc == 83 || profile_idc == 86) {
+        unsigned chroma_format_idc = parseUE(&br);
+        if (chroma_format_idc == 3) {
+            br.skipBits(1);  // residual_colour_transform_flag
+        }
+        parseUE(&br);  // bit_depth_luma_minus8
+        parseUE(&br);  // bit_depth_chroma_minus8
+        br.skipBits(1);  // qpprime_y_zero_transform_bypass_flag
+        CHECK_EQ(br.getBits(1), 0u);  // seq_scaling_matrix_present_flag
+    }
+
+    parseUE(&br);  // log2_max_frame_num_minus4
+    unsigned pic_order_cnt_type = parseUE(&br);
+
+    if (pic_order_cnt_type == 0) {
+        parseUE(&br);  // log2_max_pic_order_cnt_lsb_minus4
+    } else if (pic_order_cnt_type == 1) {
+        // offset_for_non_ref_pic, offset_for_top_to_bottom_field and
+        // offset_for_ref_frame are technically se(v), but since we are
+        // just skipping over them the midpoint does not matter.
+
+        br.getBits(1);  // delta_pic_order_always_zero_flag
+        parseUE(&br);  // offset_for_non_ref_pic
+        parseUE(&br);  // offset_for_top_to_bottom_field
+
+        unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
+        for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
+            parseUE(&br);  // offset_for_ref_frame
+        }
+    }
+
+    parseUE(&br);  // num_ref_frames
+    br.getBits(1);  // gaps_in_frame_num_value_allowed_flag
+
+    unsigned pic_width_in_mbs_minus1 = parseUE(&br);
+    unsigned pic_height_in_map_units_minus1 = parseUE(&br);
+    unsigned frame_mbs_only_flag = br.getBits(1);
+
+    *width = pic_width_in_mbs_minus1 * 16 + 16;
+
+    *height = (2 - frame_mbs_only_flag)
+        * (pic_height_in_map_units_minus1 * 16 + 16);
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index 389180c..52a391f 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -338,10 +338,15 @@
 
     MediaBuffer *outputBuffer;
     CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
+    uint8_t *outPtr = (uint8_t *) outputBuffer->data();
+    uint32_t dataLength = outputBuffer->size();
 
-    // Add 4 bytes for the start code 0x00000001
-    uint8_t *outPtr = (uint8_t *) outputBuffer->data() + 4;
-    uint32_t dataLength = outputBuffer->size() - 4;
+    if (!mSpsPpsHeaderReceived && mNumInputFrames < 0) {
+        // 4 bytes are reserved for holding the start code 0x00000001
+        // of the sequence parameter set at the beginning.
+        outPtr += 4;
+        dataLength -= 4;
+    }
 
     int32_t type;
     AVCEnc_Status encoderStatus = AVCENC_SUCCESS;
@@ -358,7 +363,7 @@
             switch (type) {
                 case AVC_NALTYPE_SPS:
                     ++mNumInputFrames;
-                    memcpy(outputBuffer->data(), "\x00\x00\x00\x01", 4);
+                    memcpy((uint8_t *)outputBuffer->data(), "\x00\x00\x00\x01", 4);
                     outputBuffer->set_range(0, dataLength + 4);
                     outPtr += (dataLength + 4);  // 4 bytes for next start code
                     dataLength = outputBuffer->size() -
diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk
index b9ba1be..2b63235 100644
--- a/media/libstagefright/colorconversion/Android.mk
+++ b/media/libstagefright/colorconversion/Android.mk
@@ -6,7 +6,8 @@
         SoftwareRenderer.cpp
 
 LOCAL_C_INCLUDES := \
-        $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
+        $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+        $(TOP)/hardware/msm7k
 
 LOCAL_SHARED_LIBRARIES :=       \
         libbinder               \
@@ -17,6 +18,11 @@
         libsurfaceflinger_client\
         libcamera_client
 
+# ifeq ($(TARGET_BOARD_PLATFORM),msm7k)
+ifeq ($(TARGET_PRODUCT),passion)
+	LOCAL_CFLAGS += -DHAS_YCBCR420_SP_ADRENO
+endif
+
 LOCAL_MODULE:= libstagefright_color_conversion
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index a6dbf69..507fa5a 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -22,65 +22,172 @@
 #include <binder/MemoryHeapBase.h>
 #include <binder/MemoryHeapPmem.h>
 #include <media/stagefright/MediaDebug.h>
-#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
+#include <ui/android_native_buffer.h>
+#include <ui/GraphicBufferMapper.h>
+
+// XXX: Temporary hack to allow referencing the _ADRENO pixel format here.
+#include <libgralloc-qsd8k/gralloc_priv.h>
 
 namespace android {
 
 SoftwareRenderer::SoftwareRenderer(
         OMX_COLOR_FORMATTYPE colorFormat,
-        const sp<ISurface> &surface,
+        const sp<Surface> &surface,
         size_t displayWidth, size_t displayHeight,
         size_t decodedWidth, size_t decodedHeight)
     : mColorFormat(colorFormat),
-      mConverter(colorFormat, OMX_COLOR_Format16bitRGB565),
-      mISurface(surface),
+      mConverter(NULL),
+      mYUVMode(None),
+      mSurface(surface),
       mDisplayWidth(displayWidth),
       mDisplayHeight(displayHeight),
       mDecodedWidth(decodedWidth),
-      mDecodedHeight(decodedHeight),
-      mFrameSize(mDecodedWidth * mDecodedHeight * 2),  // RGB565
-      mIndex(0) {
-    mMemoryHeap = new MemoryHeapBase("/dev/pmem_adsp", 2 * mFrameSize);
-    if (mMemoryHeap->heapID() < 0) {
-        LOGI("Creating physical memory heap failed, reverting to regular heap.");
-        mMemoryHeap = new MemoryHeapBase(2 * mFrameSize);
-    } else {
-        sp<MemoryHeapPmem> pmemHeap = new MemoryHeapPmem(mMemoryHeap);
-        pmemHeap->slap();
-        mMemoryHeap = pmemHeap;
+      mDecodedHeight(decodedHeight) {
+    LOGI("input format = %d", mColorFormat);
+    LOGI("display = %d x %d, decoded = %d x %d",
+            mDisplayWidth, mDisplayHeight, mDecodedWidth, mDecodedHeight);
+
+    int halFormat;
+    switch (mColorFormat) {
+#if HAS_YCBCR420_SP_ADRENO
+        case OMX_COLOR_FormatYUV420Planar:
+        {
+            halFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO;
+            mYUVMode = YUV420ToYUV420sp;
+            break;
+        }
+
+        case 0x7fa30c00:
+        {
+            halFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO;
+            mYUVMode = YUV420spToYUV420sp;
+            break;
+        }
+#endif
+
+        default:
+            halFormat = HAL_PIXEL_FORMAT_RGB_565;
+
+            mConverter = new ColorConverter(
+                    mColorFormat, OMX_COLOR_Format16bitRGB565);
+            CHECK(mConverter->isValid());
+            break;
     }
 
-    CHECK(mISurface.get() != NULL);
+    CHECK(mSurface.get() != NULL);
     CHECK(mDecodedWidth > 0);
     CHECK(mDecodedHeight > 0);
-    CHECK(mMemoryHeap->heapID() >= 0);
-    CHECK(mConverter.isValid());
+    CHECK(mConverter == NULL || mConverter->isValid());
 
-    ISurface::BufferHeap bufferHeap(
-            mDisplayWidth, mDisplayHeight,
-            mDecodedWidth, mDecodedHeight,
-            PIXEL_FORMAT_RGB_565,
-            mMemoryHeap);
+    CHECK_EQ(0,
+            native_window_set_usage(
+            mSurface.get(),
+            GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN
+            | GRALLOC_USAGE_HW_TEXTURE));
 
-    status_t err = mISurface->registerBuffers(bufferHeap);
-    CHECK_EQ(err, OK);
+    CHECK_EQ(0, native_window_set_buffer_count(mSurface.get(), 2));
+
+    // Width must be multiple of 32???
+    CHECK_EQ(0, native_window_set_buffers_geometry(
+                mSurface.get(), mDecodedWidth, mDecodedHeight,
+                halFormat));
 }
 
 SoftwareRenderer::~SoftwareRenderer() {
-    mISurface->unregisterBuffers();
+    delete mConverter;
+    mConverter = NULL;
+}
+
+static inline size_t ALIGN(size_t x, size_t alignment) {
+    return (x + alignment - 1) & ~(alignment - 1);
 }
 
 void SoftwareRenderer::render(
         const void *data, size_t size, void *platformPrivate) {
-    size_t offset = mIndex * mFrameSize;
-    void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
+    android_native_buffer_t *buf;
+    CHECK_EQ(0, mSurface->dequeueBuffer(mSurface.get(), &buf));
+    CHECK_EQ(0, mSurface->lockBuffer(mSurface.get(), buf));
 
-    mConverter.convert(
-            mDecodedWidth, mDecodedHeight,
-            data, 0, dst, 2 * mDecodedWidth);
+    GraphicBufferMapper &mapper = GraphicBufferMapper::get();
 
-    mISurface->postBuffer(offset);
-    mIndex = 1 - mIndex;
+    Rect bounds(mDecodedWidth, mDecodedHeight);
+
+    void *dst;
+    CHECK_EQ(0, mapper.lock(
+                buf->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, &dst));
+
+    if (mConverter) {
+        mConverter->convert(
+                mDecodedWidth, mDecodedHeight,
+                data, 0, dst, buf->stride * 2);
+    } else if (mYUVMode == YUV420spToYUV420sp) {
+        // Input and output are both YUV420sp, but the alignment requirements
+        // are different.
+        size_t srcYStride = mDecodedWidth;
+        const uint8_t *srcY = (const uint8_t *)data;
+        uint8_t *dstY = (uint8_t *)dst;
+        for (size_t i = 0; i < mDecodedHeight; ++i) {
+            memcpy(dstY, srcY, mDecodedWidth);
+            srcY += srcYStride;
+            dstY += buf->stride;
+        }
+
+        size_t srcUVStride = (mDecodedWidth + 1) & ~1;
+        size_t dstUVStride = ALIGN(mDecodedWidth / 2, 32) * 2;
+
+        const uint8_t *srcUV = (const uint8_t *)data
+            + mDecodedHeight * mDecodedWidth;
+
+        size_t dstUVOffset = ALIGN(ALIGN(mDecodedHeight, 32) * buf->stride, 4096);
+        uint8_t *dstUV = (uint8_t *)dst + dstUVOffset;
+
+        for (size_t i = 0; i < (mDecodedHeight + 1) / 2; ++i) {
+            memcpy(dstUV, srcUV, (mDecodedWidth + 1) & ~1);
+            srcUV += srcUVStride;
+            dstUV += dstUVStride;
+        }
+    } else if (mYUVMode == YUV420ToYUV420sp) {
+        // Input is YUV420 planar, output is YUV420sp, adhere to proper
+        // alignment requirements.
+        size_t srcYStride = mDecodedWidth;
+        const uint8_t *srcY = (const uint8_t *)data;
+        uint8_t *dstY = (uint8_t *)dst;
+        for (size_t i = 0; i < mDecodedHeight; ++i) {
+            memcpy(dstY, srcY, mDecodedWidth);
+            srcY += srcYStride;
+            dstY += buf->stride;
+        }
+
+        size_t srcUVStride = (mDecodedWidth + 1) / 2;
+        size_t dstUVStride = ALIGN(mDecodedWidth / 2, 32) * 2;
+
+        const uint8_t *srcU = (const uint8_t *)data
+            + mDecodedHeight * mDecodedWidth;
+
+        const uint8_t *srcV =
+            srcU + ((mDecodedWidth + 1) / 2) * ((mDecodedHeight + 1) / 2);
+
+        size_t dstUVOffset = ALIGN(ALIGN(mDecodedHeight, 32) * buf->stride, 4096);
+        uint8_t *dstUV = (uint8_t *)dst + dstUVOffset;
+
+        for (size_t i = 0; i < (mDecodedHeight + 1) / 2; ++i) {
+            for (size_t j = 0; j < (mDecodedWidth + 1) / 2; ++j) {
+                dstUV[2 * j + 1] = srcU[j];
+                dstUV[2 * j] = srcV[j];
+            }
+            srcU += srcUVStride;
+            srcV += srcUVStride;
+            dstUV += dstUVStride;
+        }
+    } else {
+        memcpy(dst, data, size);
+    }
+
+    CHECK_EQ(0, mapper.unlock(buf->handle));
+
+    CHECK_EQ(0, mSurface->queueBuffer(mSurface.get(), buf));
+    buf = NULL;
 }
 
 }  // namespace android
diff --git a/media/libstagefright/mpeg2ts/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp
similarity index 100%
rename from media/libstagefright/mpeg2ts/ABitReader.cpp
rename to media/libstagefright/foundation/ABitReader.cpp
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index 35eea7e..f6a8a52 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -3,6 +3,7 @@
 
 LOCAL_SRC_FILES:=               \
     AAtomizer.cpp               \
+    ABitReader.cpp              \
     ABuffer.cpp                 \
     ADebug.cpp                  \
     AHandler.cpp                \
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index ca0c68c..da340f7 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -142,11 +142,22 @@
     mSize = size;
 
     if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
+        free(mData);
+        mData = NULL;
+
         return false;
     }
 
-    if (header.flags & 0x80) {
+    if (header.version_major == 4) {
+        if (!removeUnsynchronizationV2_4()) {
+            free(mData);
+            mData = NULL;
+
+            return false;
+        }
+    } else if (header.flags & 0x80) {
         LOGV("removing unsynchronization");
+
         removeUnsynchronization();
     }
 
@@ -243,6 +254,74 @@
     }
 }
 
+static void WriteSyncsafeInteger(uint8_t *dst, size_t x) {
+    for (size_t i = 0; i < 4; ++i) {
+        dst[3 - i] = (x & 0x7f);
+        x >>= 7;
+    }
+}
+
+bool ID3::removeUnsynchronizationV2_4() {
+    size_t oldSize = mSize;
+
+    size_t offset = 0;
+    while (offset + 10 <= mSize) {
+        if (!memcmp(&mData[offset], "\0\0\0\0", 4)) {
+            break;
+        }
+
+        size_t dataSize;
+        if (!ParseSyncsafeInteger(&mData[offset + 4], &dataSize)) {
+            return false;
+        }
+
+        if (offset + dataSize + 10 > mSize) {
+            return false;
+        }
+
+        uint16_t flags = U16_AT(&mData[offset + 8]);
+        uint16_t prevFlags = flags;
+
+        if (flags & 1) {
+            // Strip data length indicator
+
+            memmove(&mData[offset + 10], &mData[offset + 14], mSize - offset - 14);
+            mSize -= 4;
+            dataSize -= 4;
+
+            flags &= ~1;
+        }
+
+        if (flags & 2) {
+            // Unsynchronization added.
+
+            for (size_t i = 0; i + 1 < dataSize; ++i) {
+                if (mData[offset + 10 + i] == 0xff
+                        && mData[offset + 11 + i] == 0x00) {
+                    memmove(&mData[offset + 11 + i], &mData[offset + 12 + i],
+                            mSize - offset - 12 - i);
+                    --mSize;
+                    --dataSize;
+                }
+            }
+
+            flags &= ~2;
+        }
+
+        if (flags != prevFlags) {
+            WriteSyncsafeInteger(&mData[offset + 4], dataSize);
+            mData[offset + 8] = flags >> 8;
+            mData[offset + 9] = flags & 0xff;
+        }
+
+        offset += 10 + dataSize;
+    }
+
+    memset(&mData[mSize], 0, oldSize - mSize);
+
+    return true;
+}
+
 ID3::Iterator::Iterator(const ID3 &parent, const char *id)
     : mParent(parent),
       mID(NULL),
@@ -529,10 +608,11 @@
 
             uint16_t flags = U16_AT(&mParent.mData[mOffset + 8]);
 
-            if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000e))
+            if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000c))
                 || (mParent.mVersion == ID3_V2_3 && (flags & 0x00c0))) {
-                // Compression, Encryption or per-Frame unsynchronization
-                // are not supported at this time.
+                // Compression or encryption are not supported at this time.
+                // Per-frame unsynchronization and data-length indicator
+                // have already been taken care of.
 
                 LOGV("Skipping unsupported frame (compression, encryption "
                      "or per-frame unsynchronization flagged");
diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h
index db49fe4..1cdf36d 100644
--- a/media/libstagefright/include/AMRExtractor.h
+++ b/media/libstagefright/include/AMRExtractor.h
@@ -22,6 +22,7 @@
 
 namespace android {
 
+struct AMessage;
 class String8;
 
 class AMRExtractor : public MediaExtractor {
@@ -49,7 +50,8 @@
 };
 
 bool SniffAMR(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
index 55efd41..7020564 100644
--- a/media/libstagefright/include/ARTSPController.h
+++ b/media/libstagefright/include/ARTSPController.h
@@ -19,6 +19,7 @@
 #define A_RTSP_CONTROLLER_H_
 
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
 #include <media/stagefright/MediaExtractor.h>
 
 namespace android {
@@ -32,18 +33,40 @@
     status_t connect(const char *url);
     void disconnect();
 
+    void seek(int64_t timeUs);
+
     virtual size_t countTracks();
     virtual sp<MediaSource> getTrack(size_t index);
 
     virtual sp<MetaData> getTrackMetaData(
             size_t index, uint32_t flags);
 
+    void onMessageReceived(const sp<AMessage> &msg);
+
 protected:
     virtual ~ARTSPController();
 
 private:
+    enum {
+        kWhatConnectDone    = 'cdon',
+        kWhatDisconnectDone = 'ddon',
+    };
+
+    enum State {
+        DISCONNECTED,
+        CONNECTED,
+        CONNECTING,
+    };
+
+    Mutex mLock;
+    Condition mCondition;
+
+    State mState;
+    status_t mConnectionResult;
+
     sp<ALooper> mLooper;
     sp<MyHandler> mHandler;
+    sp<AHandlerReflector<ARTSPController> > mReflector;
 
     DISALLOW_EVIL_CONSTRUCTORS(ARTSPController);
 };
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 55e2c36..f5df1b5 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -76,6 +76,7 @@
     bool isPlaying() const;
 
     void setISurface(const sp<ISurface> &isurface);
+    void setSurface(const sp<Surface> &surface);
     void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
     status_t setLooping(bool shouldLoop);
 
@@ -117,6 +118,7 @@
     wp<MediaPlayerBase> mListener;
 
     sp<ISurface> mISurface;
+    sp<Surface> mSurface;
     sp<MediaPlayerBase::AudioSink> mAudioSink;
 
     SystemTimeSource mSystemTimeSource;
@@ -180,6 +182,7 @@
 
     sp<ALooper> mLooper;
     sp<ARTSPController> mRTSPController;
+    int64_t mRTSPTimeOffset;
     sp<ARTPSession> mRTPSession;
     sp<UDPPusher> mRTPPusher, mRTCPPusher;
 
@@ -219,6 +222,7 @@
     status_t seekTo_l(int64_t timeUs);
     status_t pause_l();
     void initRenderer_l();
+    void notifyVideoSize_l();
     void seekAudioIfNecessary_l();
 
     void cancelPlayerEvents(bool keepBufferingGoing = false);
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
index c6b1a8b..7ddbb41 100644
--- a/media/libstagefright/include/ID3.h
+++ b/media/libstagefright/include/ID3.h
@@ -80,6 +80,7 @@
     bool parseV1(const sp<DataSource> &source);
     bool parseV2(const sp<DataSource> &source);
     void removeUnsynchronization();
+    bool removeUnsynchronizationV2_4();
 
     static bool ParseSyncsafeInteger(const uint8_t encoded[4], size_t *x);
 
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index 3ce6df3..0e6ccde 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -22,13 +22,14 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class String8;
 
 class MP3Extractor : public MediaExtractor {
 public:
     // Extractor assumes ownership of "source".
-    MP3Extractor(const sp<DataSource> &source);
+    MP3Extractor(const sp<DataSource> &source, const sp<AMessage> &meta);
 
     virtual size_t countTracks();
     virtual sp<MediaSource> getTrack(size_t index);
@@ -52,7 +53,8 @@
 };
 
 bool SniffMP3(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *meta);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index c96973b..1bf4cd1 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -9,6 +9,7 @@
 
 namespace android {
 
+struct AMessage;
 struct AnotherPacketSource;
 struct ATSParser;
 struct DataSource;
@@ -47,7 +48,8 @@
 };
 
 bool SniffMPEG2TS(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index c8663d5..1c9cc7e 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -23,6 +23,7 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class SampleTable;
 class String8;
@@ -75,7 +76,8 @@
 };
 
 bool SniffMPEG4(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/OggExtractor.h b/media/libstagefright/include/OggExtractor.h
index 7066669..1eda025 100644
--- a/media/libstagefright/include/OggExtractor.h
+++ b/media/libstagefright/include/OggExtractor.h
@@ -22,6 +22,7 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class String8;
 
@@ -53,7 +54,8 @@
 };
 
 bool SniffOgg(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 9eed089..8d58056 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -24,14 +24,14 @@
 
 namespace android {
 
-class ISurface;
+class Surface;
 class MemoryHeapBase;
 
 class SoftwareRenderer : public VideoRenderer {
 public:
     SoftwareRenderer(
             OMX_COLOR_FORMATTYPE colorFormat,
-            const sp<ISurface> &surface,
+            const sp<Surface> &surface,
             size_t displayWidth, size_t displayHeight,
             size_t decodedWidth, size_t decodedHeight);
 
@@ -41,14 +41,18 @@
             const void *data, size_t size, void *platformPrivate);
 
 private:
+    enum YUVMode {
+        None,
+        YUV420ToYUV420sp,
+        YUV420spToYUV420sp,
+    };
+
     OMX_COLOR_FORMATTYPE mColorFormat;
-    ColorConverter mConverter;
-    sp<ISurface> mISurface;
+    ColorConverter *mConverter;
+    YUVMode mYUVMode;
+    sp<Surface> mSurface;
     size_t mDisplayWidth, mDisplayHeight;
     size_t mDecodedWidth, mDecodedHeight;
-    size_t mFrameSize;
-    sp<MemoryHeapBase> mMemoryHeap;
-    int mIndex;
 
     SoftwareRenderer(const SoftwareRenderer &);
     SoftwareRenderer &operator=(const SoftwareRenderer &);
diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
index 3e847b9..df6d3e7 100644
--- a/media/libstagefright/include/WAVExtractor.h
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -22,6 +22,7 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class String8;
 
@@ -58,7 +59,8 @@
 };
 
 bool SniffWAV(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
new file mode 100644
index 0000000..cc405b5
--- /dev/null
+++ b/media/libstagefright/include/avc_utils.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AVC_UTILS_H_
+
+#define AVC_UTILS_H_
+
+#include <media/stagefright/foundation/ABuffer.h>
+
+namespace android {
+
+void FindAVCDimensions(
+        const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height);
+
+}  // namespace android
+
+#endif  // AVC_UTILS_H_
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 71f6587..7c7d69e 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -579,7 +579,8 @@
 }
 
 bool SniffMatroska(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     DataSourceReader reader(source);
     mkvparser::EBMLHeader ebmlHeader;
     long long pos;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
index 7471848..fa20b84 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.h
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -27,6 +27,7 @@
 
 namespace android {
 
+struct AMessage;
 class String8;
 
 struct DataSourceReader;
@@ -69,7 +70,8 @@
 };
 
 bool SniffMatroska(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index d05975d..26a0fb3 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -16,9 +16,10 @@
 
 #include "ATSParser.h"
 
-#include "ABitReader.h"
 #include "AnotherPacketSource.h"
+#include "include/avc_utils.h"
 
+#include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -473,60 +474,6 @@
     }
 }
 
-static unsigned parseUE(ABitReader *br) {
-    unsigned numZeroes = 0;
-    while (br->getBits(1) == 0) {
-        ++numZeroes;
-    }
-
-    unsigned x = br->getBits(numZeroes);
-
-    return x + (1u << numZeroes) - 1;
-}
-
-// Determine video dimensions from the sequence parameterset.
-static void FindDimensions(
-        const sp<ABuffer> seqParamSet, int32_t *width, int32_t *height) {
-    ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
-
-    unsigned profile_idc = br.getBits(8);
-    br.skipBits(16);
-    parseUE(&br);  // seq_parameter_set_id
-
-    if (profile_idc == 100 || profile_idc == 110
-            || profile_idc == 122 || profile_idc == 144) {
-        TRESPASS();
-    }
-
-    parseUE(&br);  // log2_max_frame_num_minus4
-    unsigned pic_order_cnt_type = parseUE(&br);
-
-    if (pic_order_cnt_type == 0) {
-        parseUE(&br);  // log2_max_pic_order_cnt_lsb_minus4
-    } else if (pic_order_cnt_type == 1) {
-        br.getBits(1);  // delta_pic_order_always_zero_flag
-        parseUE(&br);  // offset_for_non_ref_pic
-        parseUE(&br);  // offset_for_top_to_bottom_field
-
-        unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
-        for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
-            parseUE(&br);  // offset_for_ref_frame
-        }
-    }
-
-    parseUE(&br);  // num_ref_frames
-    br.getBits(1);  // gaps_in_frame_num_value_allowed_flag
-
-    unsigned pic_width_in_mbs_minus1 = parseUE(&br);
-    unsigned pic_height_in_map_units_minus1 = parseUE(&br);
-    unsigned frame_mbs_only_flag = br.getBits(1);
-
-    *width = pic_width_in_mbs_minus1 * 16 + 16;
-
-    *height = (2 - frame_mbs_only_flag)
-        * (pic_height_in_map_units_minus1 * 16 + 16);
-}
-
 static sp<ABuffer> MakeAVCCodecSpecificData(
         const sp<ABuffer> &buffer, int32_t *width, int32_t *height) {
     const uint8_t *data = buffer->data();
@@ -537,7 +484,7 @@
         return NULL;
     }
 
-    FindDimensions(seqParamSet, width, height);
+    FindAVCDimensions(seqParamSet, width, height);
 
     size_t stopOffset;
     sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index b6772eb..3544b4c 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -3,7 +3,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=                 \
-        ABitReader.cpp            \
         AnotherPacketSource.cpp   \
         ATSParser.cpp             \
         MPEG2TSExtractor.cpp      \
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index b287c95..56ca375 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -174,7 +174,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffMPEG2TS(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
 #if 0
     char header;
     if (source->readAt(0, &header, 1) != 1 || header != 0x47) {
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 6de761f..88b9605 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -495,12 +495,17 @@
     }
 
     if (!impl) {
+#if 0
         LOGW("Using software renderer.");
         impl = new SoftwareRenderer(
                 colorFormat,
                 surface,
                 displayWidth, displayHeight,
                 encodedWidth, encodedHeight);
+#else
+        CHECK(!"Should not be here.");
+        return NULL;
+#endif
     }
 
     return new OMXRenderer(impl);
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
new file mode 100644
index 0000000..7e633d7
--- /dev/null
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AMPEG4ElementaryAssembler.h"
+
+#include "ARTPSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include <stdint.h>
+
+#define BE_VERBOSE      0
+
+namespace android {
+
+// static
+AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(const sp<AMessage> &notify)
+    : mNotifyMsg(notify),
+      mAccessUnitRTPTime(0),
+      mNextExpectedSeqNoValid(false),
+      mNextExpectedSeqNo(0),
+      mAccessUnitDamaged(false) {
+}
+
+AMPEG4ElementaryAssembler::~AMPEG4ElementaryAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket(
+        const sp<ARTPSource> &source) {
+    List<sp<ABuffer> > *queue = source->queue();
+
+    if (queue->empty()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    if (mNextExpectedSeqNoValid) {
+        List<sp<ABuffer> >::iterator it = queue->begin();
+        while (it != queue->end()) {
+            if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+                break;
+            }
+
+            it = queue->erase(it);
+        }
+
+        if (queue->empty()) {
+            return NOT_ENOUGH_DATA;
+        }
+    }
+
+    sp<ABuffer> buffer = *queue->begin();
+
+    if (!mNextExpectedSeqNoValid) {
+        mNextExpectedSeqNoValid = true;
+        mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+    } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+#if BE_VERBOSE
+        LOG(VERBOSE) << "Not the sequence number I expected";
+#endif
+
+        return WRONG_SEQUENCE_NUMBER;
+    }
+
+    uint32_t rtpTime;
+    CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+    if (mPackets.size() > 0 && rtpTime != mAccessUnitRTPTime) {
+        submitAccessUnit();
+    }
+    mAccessUnitRTPTime = rtpTime;
+
+    mPackets.push_back(buffer);
+    // hexdump(buffer->data(), buffer->size());
+
+    queue->erase(queue->begin());
+    ++mNextExpectedSeqNo;
+
+    return OK;
+}
+
+void AMPEG4ElementaryAssembler::submitAccessUnit() {
+    CHECK(!mPackets.empty());
+
+#if BE_VERBOSE
+    LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)";
+#endif
+
+    uint64_t ntpTime;
+    CHECK((*mPackets.begin())->meta()->findInt64(
+                "ntp-time", (int64_t *)&ntpTime));
+
+    size_t totalSize = 0;
+    for (List<sp<ABuffer> >::iterator it = mPackets.begin();
+         it != mPackets.end(); ++it) {
+        totalSize += (*it)->size();
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(totalSize);
+    size_t offset = 0;
+    for (List<sp<ABuffer> >::iterator it = mPackets.begin();
+         it != mPackets.end(); ++it) {
+        sp<ABuffer> nal = *it;
+        memcpy(accessUnit->data() + offset, nal->data(), nal->size());
+        offset += nal->size();
+    }
+
+    accessUnit->meta()->setInt64("ntp-time", ntpTime);
+
+#if 0
+    printf(mAccessUnitDamaged ? "X" : ".");
+    fflush(stdout);
+#endif
+
+    if (mAccessUnitDamaged) {
+        accessUnit->meta()->setInt32("damaged", true);
+    }
+
+    mPackets.clear();
+    mAccessUnitDamaged = false;
+
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setObject("access-unit", accessUnit);
+    msg->post();
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::assembleMore(
+        const sp<ARTPSource> &source) {
+    AssemblyStatus status = addPacket(source);
+    if (status == MALFORMED_PACKET) {
+        mAccessUnitDamaged = true;
+    }
+    return status;
+}
+
+void AMPEG4ElementaryAssembler::packetLost() {
+    CHECK(mNextExpectedSeqNoValid);
+    LOG(VERBOSE) << "packetLost (expected " << mNextExpectedSeqNo << ")";
+
+    ++mNextExpectedSeqNo;
+
+    mAccessUnitDamaged = true;
+}
+
+void AMPEG4ElementaryAssembler::onByeReceived() {
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setInt32("eos", true);
+    msg->post();
+}
+
+}  // namespace android
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
new file mode 100644
index 0000000..1566d00
--- /dev/null
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_MPEG4_ELEM_ASSEMBLER_H_
+
+#define A_MPEG4_ELEM_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+
+struct AMPEG4ElementaryAssembler : public ARTPAssembler {
+    AMPEG4ElementaryAssembler(const sp<AMessage> &notify);
+
+protected:
+    virtual ~AMPEG4ElementaryAssembler();
+
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+    virtual void onByeReceived();
+    virtual void packetLost();
+
+private:
+    sp<AMessage> mNotifyMsg;
+
+    uint32_t mAccessUnitRTPTime;
+    bool mNextExpectedSeqNoValid;
+    uint32_t mNextExpectedSeqNo;
+    bool mAccessUnitDamaged;
+    List<sp<ABuffer> > mPackets;
+
+    AssemblyStatus addPacket(const sp<ARTPSource> &source);
+    void submitAccessUnit();
+
+    DISALLOW_EVIL_CONSTRUCTORS(AMPEG4ElementaryAssembler);
+};
+
+}  // namespace android
+
+#endif  // A_MPEG4_ELEM_ASSEMBLER_H_
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 224b4bf..b930184 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -18,6 +18,11 @@
 
 #include "ASessionDescription.h"
 
+#include "avc_utils.h"
+
+#include <ctype.h>
+
+#include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -37,6 +42,10 @@
     size_t keyLen = strlen(key);
 
     for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
         const char *colonPos = strchr(s, ';');
 
         size_t len =
@@ -90,7 +99,11 @@
     return buffer;
 }
 
-static sp<ABuffer> MakeAVCCodecSpecificData(const char *params) {
+static sp<ABuffer> MakeAVCCodecSpecificData(
+        const char *params, int32_t *width, int32_t *height) {
+    *width = 0;
+    *height = 0;
+
     AString val;
     if (!GetAttribute(params, "profile-level-id", &val)) {
         return NULL;
@@ -172,6 +185,11 @@
         memcpy(out, nal->data(), nal->size());
 
         out += nal->size();
+
+        if (i == 0) {
+            FindAVCDimensions(nal, width, height);
+            LOG(INFO) << "dimensions " << *width << "x" << *height;
+        }
     }
 
     *out++ = numPicParameterSets;
@@ -187,7 +205,7 @@
         out += nal->size();
     }
 
-    hexdump(csd->data(), csd->size());
+    // hexdump(csd->data(), csd->size());
 
     return csd;
 }
@@ -224,7 +242,162 @@
     csd->data()[sizeof(kStaticESDS)] = (x >> 8) & 0xff;
     csd->data()[sizeof(kStaticESDS) + 1] = x & 0xff;
 
-    hexdump(csd->data(), csd->size());
+    // hexdump(csd->data(), csd->size());
+
+    return csd;
+}
+
+static size_t GetSizeWidth(size_t x) {
+    size_t n = 1;
+    while (x > 127) {
+        ++n;
+        x >>= 7;
+    }
+    return n;
+}
+
+static uint8_t *EncodeSize(uint8_t *dst, size_t x) {
+    while (x > 127) {
+        *dst++ = (x & 0x7f) | 0x80;
+        x >>= 7;
+    }
+    *dst++ = x;
+    return dst;
+}
+
+static bool ExtractDimensionsFromVOLHeader(
+        const sp<ABuffer> &config, int32_t *width, int32_t *height) {
+    *width = 0;
+    *height = 0;
+
+    const uint8_t *ptr = config->data();
+    size_t offset = 0;
+    bool foundVOL = false;
+    while (offset + 3 < config->size()) {
+        if (memcmp("\x00\x00\x01", &ptr[offset], 3)
+                || (ptr[offset + 3] & 0xf0) != 0x20) {
+            ++offset;
+            continue;
+        }
+
+        foundVOL = true;
+        break;
+    }
+
+    if (!foundVOL) {
+        return false;
+    }
+
+    ABitReader br(&ptr[offset + 4], config->size() - offset - 4);
+    br.skipBits(1);  // random_accessible_vol
+    unsigned video_object_type_indication = br.getBits(8);
+
+    CHECK_NE(video_object_type_indication,
+             0x21u /* Fine Granularity Scalable */);
+
+    unsigned video_object_layer_verid;
+    unsigned video_object_layer_priority;
+    if (br.getBits(1)) {
+        video_object_layer_verid = br.getBits(4);
+        video_object_layer_priority = br.getBits(3);
+    }
+    unsigned aspect_ratio_info = br.getBits(4);
+    if (aspect_ratio_info == 0x0f /* extended PAR */) {
+        br.skipBits(8);  // par_width
+        br.skipBits(8);  // par_height
+    }
+    if (br.getBits(1)) {  // vol_control_parameters
+        br.skipBits(2);  // chroma_format
+        br.skipBits(1);  // low_delay
+        if (br.getBits(1)) {  // vbv_parameters
+            TRESPASS();
+        }
+    }
+    unsigned video_object_layer_shape = br.getBits(2);
+    CHECK_EQ(video_object_layer_shape, 0x00u /* rectangular */);
+
+    CHECK(br.getBits(1));  // marker_bit
+    unsigned vop_time_increment_resolution = br.getBits(16);
+    CHECK(br.getBits(1));  // marker_bit
+
+    if (br.getBits(1)) {  // fixed_vop_rate
+        // range [0..vop_time_increment_resolution)
+
+        // vop_time_increment_resolution
+        // 2 => 0..1, 1 bit
+        // 3 => 0..2, 2 bits
+        // 4 => 0..3, 2 bits
+        // 5 => 0..4, 3 bits
+        // ...
+
+        CHECK_GT(vop_time_increment_resolution, 0u);
+        --vop_time_increment_resolution;
+
+        unsigned numBits = 0;
+        while (vop_time_increment_resolution > 0) {
+            ++numBits;
+            vop_time_increment_resolution >>= 1;
+        }
+
+        br.skipBits(numBits);  // fixed_vop_time_increment
+    }
+
+    CHECK(br.getBits(1));  // marker_bit
+    unsigned video_object_layer_width = br.getBits(13);
+    CHECK(br.getBits(1));  // marker_bit
+    unsigned video_object_layer_height = br.getBits(13);
+    CHECK(br.getBits(1));  // marker_bit
+
+    unsigned interlaced = br.getBits(1);
+
+    *width = video_object_layer_width;
+    *height = video_object_layer_height;
+
+    LOG(INFO) << "VOL dimensions = " << *width << "x" << *height;
+
+    return true;
+}
+
+sp<ABuffer> MakeMPEG4VideoCodecSpecificData(
+        const char *params, int32_t *width, int32_t *height) {
+    *width = 0;
+    *height = 0;
+
+    AString val;
+    CHECK(GetAttribute(params, "config", &val));
+
+    sp<ABuffer> config = decodeHex(val);
+    CHECK(config != NULL);
+
+    if (!ExtractDimensionsFromVOLHeader(config, width, height)) {
+        return NULL;
+    }
+
+    size_t len1 = config->size() + GetSizeWidth(config->size()) + 1;
+    size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13;
+    size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3;
+
+    sp<ABuffer> csd = new ABuffer(len3);
+    uint8_t *dst = csd->data();
+    *dst++ = 0x03;
+    dst = EncodeSize(dst, len2 + 3);
+    *dst++ = 0x00;  // ES_ID
+    *dst++ = 0x00;
+    *dst++ = 0x00;  // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+    *dst++ = 0x04;
+    dst = EncodeSize(dst, len1 + 13);
+    *dst++ = 0x01;  // Video ISO/IEC 14496-2 Simple Profile
+    for (size_t i = 0; i < 12; ++i) {
+        *dst++ = 0x00;
+    }
+
+    *dst++ = 0x05;
+    dst = EncodeSize(dst, config->size());
+    memcpy(dst, config->data(), config->size());
+    dst += config->size();
+
+    // hexdump(csd->data(), csd->size());
 
     return csd;
 }
@@ -233,9 +406,7 @@
         const sp<ASessionDescription> &sessionDesc, size_t index)
     : mInitCheck(NO_INIT),
       mFormat(new MetaData),
-      mEOSResult(OK),
-      mFirstAccessUnit(true),
-      mFirstAccessUnitNTP(0) {
+      mEOSResult(OK) {
     unsigned long PT;
     AString desc;
     AString params;
@@ -253,25 +424,42 @@
         mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
 
         int32_t width, height;
-        sessionDesc->getDimensions(index, PT, &width, &height);
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            width = -1;
+            height = -1;
+        }
 
-        mFormat->setInt32(kKeyWidth, width);
-        mFormat->setInt32(kKeyHeight, height);
-
+        int32_t encWidth, encHeight;
         sp<ABuffer> codecSpecificData =
-            MakeAVCCodecSpecificData(params.c_str());
+            MakeAVCCodecSpecificData(params.c_str(), &encWidth, &encHeight);
 
         if (codecSpecificData != NULL) {
+            if (width < 0) {
+                // If no explicit width/height given in the sdp, use the dimensions
+                // extracted from the first sequence parameter set.
+                width = encWidth;
+                height = encHeight;
+            }
+
             mFormat->setData(
                     kKeyAVCC, 0,
                     codecSpecificData->data(), codecSpecificData->size());
+        } else if (width < 0) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
         }
+
+        mFormat->setInt32(kKeyWidth, width);
+        mFormat->setInt32(kKeyHeight, height);
     } else if (!strncmp(desc.c_str(), "H263-2000/", 10)
             || !strncmp(desc.c_str(), "H263-1998/", 10)) {
         mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
 
         int32_t width, height;
-        sessionDesc->getDimensions(index, PT, &width, &height);
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
 
         mFormat->setInt32(kKeyWidth, width);
         mFormat->setInt32(kKeyHeight, height);
@@ -317,6 +505,36 @@
         if (sampleRate != 16000 || numChannels != 1) {
             mInitCheck = ERROR_UNSUPPORTED;
         }
+    } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+
+        int32_t width, height;
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            width = -1;
+            height = -1;
+        }
+
+        int32_t encWidth, encHeight;
+        sp<ABuffer> codecSpecificData =
+            MakeMPEG4VideoCodecSpecificData(
+                    params.c_str(), &encWidth, &encHeight);
+
+        if (codecSpecificData != NULL) {
+            mFormat->setData(
+                    kKeyESDS, 0,
+                    codecSpecificData->data(), codecSpecificData->size());
+
+            if (width < 0) {
+                width = encWidth;
+                height = encHeight;
+            }
+        } else if (width < 0) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
+
+        mFormat->setInt32(kKeyWidth, width);
+        mFormat->setInt32(kKeyHeight, height);
     } else {
         mInitCheck = ERROR_UNSUPPORTED;
     }
@@ -330,9 +548,6 @@
 }
 
 status_t APacketSource::start(MetaData *params) {
-    mFirstAccessUnit = true;
-    mFirstAccessUnitNTP = 0;
-
     return OK;
 }
 
@@ -380,25 +595,6 @@
         return;
     }
 
-    uint64_t ntpTime;
-    CHECK(buffer->meta()->findInt64(
-                "ntp-time", (int64_t *)&ntpTime));
-
-    if (mFirstAccessUnit) {
-        mFirstAccessUnit = false;
-        mFirstAccessUnitNTP = ntpTime;
-    }
-
-    if (ntpTime > mFirstAccessUnitNTP) {
-        ntpTime -= mFirstAccessUnitNTP;
-    } else {
-        ntpTime = 0;
-    }
-
-    int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
-
-    buffer->meta()->setInt64("timeUs", timeUs);
-
     Mutex::Autolock autoLock(mLock);
     mBuffers.push_back(buffer);
     mCondition.signal();
@@ -412,31 +608,9 @@
     mCondition.signal();
 }
 
-int64_t APacketSource::getQueuedDuration(bool *eos) {
+void APacketSource::flushQueue() {
     Mutex::Autolock autoLock(mLock);
-
-    *eos = (mEOSResult != OK);
-
-    if (mBuffers.empty()) {
-        return 0;
-    }
-
-    sp<ABuffer> buffer = *mBuffers.begin();
-
-    uint64_t ntpTime;
-    CHECK(buffer->meta()->findInt64(
-                "ntp-time", (int64_t *)&ntpTime));
-
-    int64_t firstTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
-
-    buffer = *--mBuffers.end();
-
-    CHECK(buffer->meta()->findInt64(
-                "ntp-time", (int64_t *)&ntpTime));
-
-    int64_t lastTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
-
-    return lastTimeUs - firstTimeUs;
+    mBuffers.clear();
 }
 
 }  // namespace android
diff --git a/media/libstagefright/rtsp/APacketSource.h b/media/libstagefright/rtsp/APacketSource.h
index 647da6e..197af3e 100644
--- a/media/libstagefright/rtsp/APacketSource.h
+++ b/media/libstagefright/rtsp/APacketSource.h
@@ -43,7 +43,7 @@
     void queueAccessUnit(const sp<ABuffer> &buffer);
     void signalEOS(status_t result);
 
-    int64_t getQueuedDuration(bool *eos);
+    void flushQueue();
 
 protected:
     virtual ~APacketSource();
@@ -58,9 +58,6 @@
     List<sp<ABuffer> > mBuffers;
     status_t mEOSResult;
 
-    bool mFirstAccessUnit;
-    uint64_t mFirstAccessUnitNTP;
-
     DISALLOW_EVIL_CONSTRUCTORS(APacketSource);
 };
 
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 469af3e..42a22b7 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -57,6 +57,8 @@
 
     int32_t mNumRTCPPacketsReceived;
     struct sockaddr_in mRemoteRTCPAddr;
+
+    bool mIsInjected;
 };
 
 ARTPConnection::ARTPConnection(uint32_t flags)
@@ -72,13 +74,15 @@
         int rtpSocket, int rtcpSocket,
         const sp<ASessionDescription> &sessionDesc,
         size_t index,
-        const sp<AMessage> &notify) {
+        const sp<AMessage> &notify,
+        bool injected) {
     sp<AMessage> msg = new AMessage(kWhatAddStream, id());
     msg->setInt32("rtp-socket", rtpSocket);
     msg->setInt32("rtcp-socket", rtcpSocket);
     msg->setObject("session-desc", sessionDesc);
     msg->setSize("index", index);
     msg->setMessage("notify", notify);
+    msg->setInt32("injected", injected);
     msg->post();
 }
 
@@ -154,6 +158,12 @@
             break;
         }
 
+        case kWhatInjectPacket:
+        {
+            onInjectPacket(msg);
+            break;
+        }
+
         default:
         {
             TRESPASS();
@@ -172,6 +182,11 @@
     CHECK(msg->findInt32("rtcp-socket", &s));
     info->mRTCPSocket = s;
 
+    int32_t injected;
+    CHECK(msg->findInt32("injected", &injected));
+
+    info->mIsInjected = injected;
+
     sp<RefBase> obj;
     CHECK(msg->findObject("session-desc", &obj));
     info->mSessionDesc = static_cast<ASessionDescription *>(obj.get());
@@ -182,7 +197,9 @@
     info->mNumRTCPPacketsReceived = 0;
     memset(&info->mRemoteRTCPAddr, 0, sizeof(info->mRemoteRTCPAddr));
 
-    postPollEvent();
+    if (!injected) {
+        postPollEvent();
+    }
 }
 
 void ARTPConnection::onRemoveStream(const sp<AMessage> &msg) {
@@ -231,6 +248,10 @@
     int maxSocket = -1;
     for (List<StreamInfo>::iterator it = mStreams.begin();
          it != mStreams.end(); ++it) {
+        if ((*it).mIsInjected) {
+            continue;
+        }
+
         FD_SET(it->mRTPSocket, &rs);
         FD_SET(it->mRTCPSocket, &rs);
 
@@ -248,6 +269,10 @@
     if (res > 0) {
         for (List<StreamInfo>::iterator it = mStreams.begin();
              it != mStreams.end(); ++it) {
+            if ((*it).mIsInjected) {
+                continue;
+            }
+
             if (FD_ISSET(it->mRTPSocket, &rs)) {
                 receive(&*it, true);
             }
@@ -301,6 +326,8 @@
 }
 
 status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
+    CHECK(!s->mIsInjected);
+
     sp<ABuffer> buffer = new ABuffer(65536);
 
     socklen_t remoteAddrLen =
@@ -321,6 +348,8 @@
 
     buffer->setRange(0, nbytes);
 
+    // LOG(INFO) << "received " << buffer->size() << " bytes.";
+
     status_t err;
     if (receiveRTP) {
         err = parseRTP(s, buffer);
@@ -557,5 +586,42 @@
     return source;
 }
 
+void ARTPConnection::injectPacket(int index, const sp<ABuffer> &buffer) {
+    sp<AMessage> msg = new AMessage(kWhatInjectPacket, id());
+    msg->setInt32("index", index);
+    msg->setObject("buffer", buffer);
+    msg->post();
+}
+
+void ARTPConnection::onInjectPacket(const sp<AMessage> &msg) {
+    int32_t index;
+    CHECK(msg->findInt32("index", &index));
+
+    sp<RefBase> obj;
+    CHECK(msg->findObject("buffer", &obj));
+
+    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+    List<StreamInfo>::iterator it = mStreams.begin();
+    while (it != mStreams.end()
+           && it->mRTPSocket != index && it->mRTCPSocket != index) {
+        ++it;
+    }
+
+    if (it == mStreams.end()) {
+        TRESPASS();
+    }
+
+    StreamInfo *s = &*it;
+
+    status_t err;
+    if (it->mRTPSocket == index) {
+        err = parseRTP(s, buffer);
+    } else {
+        ++s->mNumRTCPPacketsReceived;
+        err = parseRTCP(s, buffer);
+    }
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/rtsp/ARTPConnection.h b/media/libstagefright/rtsp/ARTPConnection.h
index c535199..77f81fa 100644
--- a/media/libstagefright/rtsp/ARTPConnection.h
+++ b/media/libstagefright/rtsp/ARTPConnection.h
@@ -38,10 +38,13 @@
     void addStream(
             int rtpSocket, int rtcpSocket,
             const sp<ASessionDescription> &sessionDesc, size_t index,
-            const sp<AMessage> &notify);
+            const sp<AMessage> &notify,
+            bool injected);
 
     void removeStream(int rtpSocket, int rtcpSocket);
 
+    void injectPacket(int index, const sp<ABuffer> &buffer);
+
     // Creates a pair of UDP datagram sockets bound to adjacent ports
     // (the rtpSocket is bound to an even port, the rtcpSocket to the
     // next higher port).
@@ -57,6 +60,7 @@
         kWhatAddStream,
         kWhatRemoveStream,
         kWhatPollStreams,
+        kWhatInjectPacket,
     };
 
     static const int64_t kSelectTimeoutUs;
@@ -72,6 +76,7 @@
     void onAddStream(const sp<AMessage> &msg);
     void onRemoveStream(const sp<AMessage> &msg);
     void onPollStreams();
+    void onInjectPacket(const sp<AMessage> &msg);
     void onSendReceiverReports();
 
     status_t receive(StreamInfo *info, bool receiveRTP);
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index e082078..d2c56f7 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -83,7 +83,8 @@
         sp<AMessage> notify = new AMessage(kWhatAccessUnitComplete, id());
         notify->setSize("track-index", mTracks.size() - 1);
 
-        mRTPConn->addStream(rtpSocket, rtcpSocket, mDesc, i, notify);
+        mRTPConn->addStream(
+                rtpSocket, rtcpSocket, mDesc, i, notify, false /* injected */);
 
         info->mPacketSource = source;
     }
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 225f6e8..775c4ee 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -20,6 +20,7 @@
 #include "AAVCAssembler.h"
 #include "AH263Assembler.h"
 #include "AMPEG4AudioAssembler.h"
+#include "AMPEG4ElementaryAssembler.h"
 #include "ASessionDescription.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
@@ -63,6 +64,9 @@
         mAssembler = new AAMRAssembler(notify, false /* isWide */, params);
     } else  if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
         mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
+    } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
+        mAssembler = new AMPEG4ElementaryAssembler(notify);
+        mIssueFIRRequests = true;
     } else {
         TRESPASS();
     }
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index d6dd597..d4eed7c 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -134,10 +134,10 @@
     return OK;
 }
 
-void ARTPWriter::stop() {
+status_t ARTPWriter::stop() {
     Mutex::Autolock autoLock(mLock);
     if (!(mFlags & kFlagStarted)) {
-        return;
+        return OK;
     }
 
     (new AMessage(kWhatStop, mReflector->id()))->post();
@@ -145,9 +145,11 @@
     while (mFlags & kFlagStarted) {
         mCondition.wait(mLock);
     }
+    return OK;
 }
 
-void ARTPWriter::pause() {
+status_t ARTPWriter::pause() {
+    return OK;
 }
 
 static void StripStartcode(MediaBuffer *buffer) {
diff --git a/media/libstagefright/rtsp/ARTPWriter.h b/media/libstagefright/rtsp/ARTPWriter.h
index b1b8b45..fdc8d23 100644
--- a/media/libstagefright/rtsp/ARTPWriter.h
+++ b/media/libstagefright/rtsp/ARTPWriter.h
@@ -40,8 +40,8 @@
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual bool reachedEOS();
     virtual status_t start(MetaData *params);
-    virtual void stop();
-    virtual void pause();
+    virtual status_t stop();
+    virtual status_t pause();
 
     virtual void onMessageReceived(const sp<AMessage> &msg);
 
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index 9826990..fd6aa62 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -19,6 +19,7 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
 
 #include <arpa/inet.h>
 #include <fcntl.h>
@@ -67,6 +68,12 @@
     msg->post();
 }
 
+void ARTSPConnection::observeBinaryData(const sp<AMessage> &reply) {
+    sp<AMessage> msg = new AMessage(kWhatObserveBinaryData, id());
+    msg->setMessage("reply", reply);
+    msg->post();
+}
+
 void ARTSPConnection::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatConnect:
@@ -89,6 +96,12 @@
             onReceiveResponse();
             break;
 
+        case kWhatObserveBinaryData:
+        {
+            CHECK(msg->findMessage("reply", &mObserveBinaryMessage));
+            break;
+        }
+
         default:
             TRESPASS();
             break;
@@ -136,6 +149,20 @@
     return true;
 }
 
+static void MakeSocketBlocking(int s, bool blocking) {
+    // Make socket non-blocking.
+    int flags = fcntl(s, F_GETFL, 0);
+    CHECK_NE(flags, -1);
+
+    if (blocking) {
+        flags &= ~O_NONBLOCK;
+    } else {
+        flags |= O_NONBLOCK;
+    }
+
+    CHECK_NE(fcntl(s, F_SETFL, flags), -1);
+}
+
 void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
     ++mConnectionID;
 
@@ -150,10 +177,7 @@
 
     mSocket = socket(AF_INET, SOCK_STREAM, 0);
 
-    // Make socket non-blocking.
-    int flags = fcntl(mSocket, F_GETFL, 0);
-    CHECK_NE(flags, -1);
-    CHECK_NE(fcntl(mSocket, F_SETFL, flags | O_NONBLOCK), -1);
+    MakeSocketBlocking(mSocket, false);
 
     AString url;
     CHECK(msg->findString("url", &url));
@@ -210,7 +234,7 @@
         mSocket = -1;
 
         flushPendingRequests();
-    } 
+    }
 
     sp<AMessage> reply;
     CHECK(msg->findMessage("reply", &reply));
@@ -347,7 +371,13 @@
     CHECK_GE(res, 0);
 
     if (res == 1) {
-        if (!receiveRTSPReponse()) {
+        MakeSocketBlocking(mSocket, true);
+
+        bool success = receiveRTSPReponse();
+
+        MakeSocketBlocking(mSocket, false);
+
+        if (!success) {
             // Something horrible, irreparable has happened.
             flushPendingRequests();
             return;
@@ -379,16 +409,13 @@
     mReceiveResponseEventPending = true;
 }
 
-bool ARTSPConnection::receiveLine(AString *line) {
-    line->clear();
-
-    bool sawCR = false;
-    for (;;) {
-        char c;
-        ssize_t n = recv(mSocket, &c, 1, 0);
+status_t ARTSPConnection::receive(void *data, size_t size) {
+    size_t offset = 0;
+    while (offset < size) {
+        ssize_t n = recv(mSocket, (uint8_t *)data + offset, size - offset, 0);
         if (n == 0) {
             // Server closed the connection.
-            return false;
+            return ERROR_IO;
         } else if (n < 0) {
             if (errno == EINTR) {
                 continue;
@@ -397,6 +424,22 @@
             TRESPASS();
         }
 
+        offset += (size_t)n;
+    }
+
+    return OK;
+}
+
+bool ARTSPConnection::receiveLine(AString *line) {
+    line->clear();
+
+    bool sawCR = false;
+    for (;;) {
+        char c;
+        if (receive(&c, 1) != OK) {
+            return false;
+        }
+
         if (sawCR && c == '\n') {
             line->erase(line->size() - 1, 1);
             return true;
@@ -404,17 +447,59 @@
 
         line->append(&c, 1);
 
+        if (c == '$' && line->size() == 1) {
+            // Special-case for interleaved binary data.
+            return true;
+        }
+
         sawCR = (c == '\r');
     }
 }
 
-bool ARTSPConnection::receiveRTSPReponse() {
-    sp<ARTSPResponse> response = new ARTSPResponse;
+sp<ABuffer> ARTSPConnection::receiveBinaryData() {
+    uint8_t x[3];
+    if (receive(x, 3) != OK) {
+        return NULL;
+    }
 
-    if (!receiveLine(&response->mStatusLine)) {
+    sp<ABuffer> buffer = new ABuffer((x[1] << 8) | x[2]);
+    if (receive(buffer->data(), buffer->size()) != OK) {
+        return NULL;
+    }
+
+    buffer->meta()->setInt32("index", (int32_t)x[0]);
+
+    return buffer;
+}
+
+bool ARTSPConnection::receiveRTSPReponse() {
+    AString statusLine;
+
+    if (!receiveLine(&statusLine)) {
         return false;
     }
 
+    if (statusLine == "$") {
+        sp<ABuffer> buffer = receiveBinaryData();
+
+        if (buffer == NULL) {
+            return false;
+        }
+
+        if (mObserveBinaryMessage != NULL) {
+            sp<AMessage> notify = mObserveBinaryMessage->dup();
+            notify->setObject("buffer", buffer);
+            notify->post();
+        } else {
+            LOG(WARNING) << "received binary data, but no one cares.";
+        }
+
+        return true;
+    }
+
+    sp<ARTSPResponse> response = new ARTSPResponse;
+    response->mStatusLine = statusLine;
+
     LOG(INFO) << "status: " << response->mStatusLine;
 
     ssize_t space1 = response->mStatusLine.find(" ");
diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h
index 3577a2f..96e0d5b 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.h
+++ b/media/libstagefright/rtsp/ARTSPConnection.h
@@ -40,6 +40,8 @@
 
     void sendRequest(const char *request, const sp<AMessage> &reply);
 
+    void observeBinaryData(const sp<AMessage> &reply);
+
 protected:
     virtual ~ARTSPConnection();
     virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -57,6 +59,7 @@
         kWhatCompleteConnection = 'comc',
         kWhatSendRequest        = 'sreq',
         kWhatReceiveResponse    = 'rres',
+        kWhatObserveBinaryData  = 'obin',
     };
 
     static const int64_t kSelectTimeoutUs;
@@ -69,6 +72,8 @@
 
     KeyedVector<int32_t, sp<AMessage> > mPendingRequests;
 
+    sp<AMessage> mObserveBinaryMessage;
+
     void onConnect(const sp<AMessage> &msg);
     void onDisconnect(const sp<AMessage> &msg);
     void onCompleteConnection(const sp<AMessage> &msg);
@@ -80,7 +85,9 @@
 
     // Return false iff something went unrecoverably wrong.
     bool receiveRTSPReponse();
+    status_t receive(void *data, size_t size);
     bool receiveLine(AString *line);
+    sp<ABuffer> receiveBinaryData();
     bool notifyResponseListener(const sp<ARTSPResponse> &response);
 
     static bool ParseURL(
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index 195323e..9df17cba 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -26,34 +26,70 @@
 namespace android {
 
 ARTSPController::ARTSPController(const sp<ALooper> &looper)
-    : mLooper(looper) {
+    : mState(DISCONNECTED),
+      mLooper(looper) {
+    mReflector = new AHandlerReflector<ARTSPController>(this);
+    looper->registerHandler(mReflector);
 }
 
 ARTSPController::~ARTSPController() {
+    CHECK_EQ((int)mState, (int)DISCONNECTED);
+    mLooper->unregisterHandler(mReflector->id());
 }
 
 status_t ARTSPController::connect(const char *url) {
-    if (mHandler != NULL) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mState != DISCONNECTED) {
         return ERROR_ALREADY_CONNECTED;
     }
 
+    sp<AMessage> msg = new AMessage(kWhatConnectDone, mReflector->id());
+
     mHandler = new MyHandler(url, mLooper);
-    mHandler->connect();
 
-    sleep(10);
+    mState = CONNECTING;
 
-    return OK;
+    mHandler->connect(msg);
+
+    while (mState == CONNECTING) {
+        mCondition.wait(mLock);
+    }
+
+    if (mState != CONNECTED) {
+        mHandler.clear();
+    }
+
+    return mConnectionResult;
 }
 
 void ARTSPController::disconnect() {
-    if (mHandler == NULL) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mState != CONNECTED) {
         return;
     }
 
-    mHandler->disconnect();
+    sp<AMessage> msg = new AMessage(kWhatDisconnectDone, mReflector->id());
+    mHandler->disconnect(msg);
+
+    while (mState == CONNECTED) {
+        mCondition.wait(mLock);
+    }
+
     mHandler.clear();
 }
 
+void ARTSPController::seek(int64_t timeUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mState != CONNECTED) {
+        return;
+    }
+
+    mHandler->seek(timeUs);
+}
+
 size_t ARTSPController::countTracks() {
     if (mHandler == NULL) {
         return 0;
@@ -75,4 +111,31 @@
     return mHandler->getPacketSource(index)->getFormat();
 }
 
+void ARTSPController::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatConnectDone:
+        {
+            Mutex::Autolock autoLock(mLock);
+
+            CHECK(msg->findInt32("result", &mConnectionResult));
+            mState = (mConnectionResult == OK) ? CONNECTED : DISCONNECTED;
+
+            mCondition.signal();
+            break;
+        }
+
+        case kWhatDisconnectDone:
+        {
+            Mutex::Autolock autoLock(mLock);
+            mState = DISCONNECTED;
+            mCondition.signal();
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
 }  // namespace android
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 8187e0c..4a8cce85 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -203,13 +203,18 @@
     }
 }
 
-void ASessionDescription::getDimensions(
+bool ASessionDescription::getDimensions(
         size_t index, unsigned long PT,
         int32_t *width, int32_t *height) const {
+    *width = 0;
+    *height = 0;
+
     char key[20];
     sprintf(key, "a=framesize:%lu", PT);
     AString value;
-    CHECK(findAttribute(index, key, &value));
+    if (!findAttribute(index, key, &value)) {
+        return false;
+    }
 
     const char *s = value.c_str();
     char *end;
@@ -221,6 +226,8 @@
     *height = strtoul(s, &end, 10);
     CHECK_GT(end, s);
     CHECK_EQ(*end, '\0');
+
+    return true;
 }
 
 bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
diff --git a/media/libstagefright/rtsp/ASessionDescription.h b/media/libstagefright/rtsp/ASessionDescription.h
index b26980f..a3fa79e 100644
--- a/media/libstagefright/rtsp/ASessionDescription.h
+++ b/media/libstagefright/rtsp/ASessionDescription.h
@@ -44,7 +44,7 @@
             size_t index, unsigned long *PT,
             AString *desc, AString *params) const;
 
-    void getDimensions(
+    bool getDimensions(
             size_t index, unsigned long PT,
             int32_t *width, int32_t *height) const;
 
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 7f3659f..ed16059 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -7,6 +7,7 @@
         AAVCAssembler.cpp           \
         AH263Assembler.cpp          \
         AMPEG4AudioAssembler.cpp    \
+        AMPEG4ElementaryAssembler.cpp \
         APacketSource.cpp           \
         ARTPAssembler.cpp           \
         ARTPConnection.cpp          \
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 8be8914..e721dd7 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -29,6 +29,8 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MediaErrors.h>
 
+#define USE_TCP_INTERLEAVED     0
+
 namespace android {
 
 struct MyHandler : public AHandler {
@@ -38,25 +40,40 @@
           mConn(new ARTSPConnection),
           mRTPConn(new ARTPConnection),
           mSessionURL(url),
-          mSetupTracksSuccessful(false) {
+          mSetupTracksSuccessful(false),
+          mSeekPending(false),
+          mFirstAccessUnit(true),
+          mFirstAccessUnitNTP(0) {
 
         mNetLooper->start(false /* runOnCallingThread */,
                           false /* canCallJava */,
                           PRIORITY_HIGHEST);
     }
 
-    void connect() {
+    void connect(const sp<AMessage> &doneMsg) {
+        mDoneMsg = doneMsg;
+
         mLooper->registerHandler(this);
         mLooper->registerHandler(mConn);
         (1 ? mNetLooper : mLooper)->registerHandler(mRTPConn);
-        sp<AMessage> reply = new AMessage('conn', id());
 
+        sp<AMessage> notify = new AMessage('biny', id());
+        mConn->observeBinaryData(notify);
+
+        sp<AMessage> reply = new AMessage('conn', id());
         mConn->connect(mSessionURL.c_str(), reply);
     }
 
-    void disconnect() {
-        sp<AMessage> reply = new AMessage('disc', id());
-        mConn->disconnect(reply);
+    void disconnect(const sp<AMessage> &doneMsg) {
+        mDoneMsg = doneMsg;
+
+        (new AMessage('abor', id()))->post();
+    }
+
+    void seek(int64_t timeUs) {
+        sp<AMessage> msg = new AMessage('seek', id());
+        msg->setInt64("time", timeUs);
+        msg->post();
     }
 
     virtual void onMessageReceived(const sp<AMessage> &msg) {
@@ -79,14 +96,14 @@
 
                     sp<AMessage> reply = new AMessage('desc', id());
                     mConn->sendRequest(request.c_str(), reply);
+                } else {
+                    (new AMessage('disc', id()))->post();
                 }
                 break;
             }
 
             case 'disc':
             {
-                LOG(INFO) << "disconnect completed";
-
                 (new AMessage('quit', id()))->post();
                 break;
             }
@@ -173,8 +190,10 @@
 
                 if (result != OK) {
                     if (track) {
-                        close(track->mRTPSocket);
-                        close(track->mRTCPSocket);
+                        if (!track->mUsingInterleavedTCP) {
+                            close(track->mRTPSocket);
+                            close(track->mRTCPSocket);
+                        }
 
                         mTracks.removeItemsAt(trackIndex);
                     }
@@ -206,7 +225,7 @@
                     mRTPConn->addStream(
                             track->mRTPSocket, track->mRTCPSocket,
                             mSessionDesc, index,
-                            notify);
+                            notify, track->mUsingInterleavedTCP);
 
                     mSetupTracksSuccessful = true;
                 }
@@ -250,8 +269,12 @@
 
                     CHECK_EQ(response->mStatusCode, 200u);
 
-                    sp<AMessage> msg = new AMessage('abor', id());
-                    msg->post(60000000ll);
+                    mDoneMsg->setInt32("result", OK);
+                    mDoneMsg->post();
+                    mDoneMsg = NULL;
+
+                    sp<AMessage> timeout = new AMessage('tiou', id());
+                    timeout->post(10000000ll);
                 } else {
                     sp<AMessage> reply = new AMessage('disc', id());
                     mConn->disconnect(reply);
@@ -301,6 +324,11 @@
 
             case 'quit':
             {
+                if (mDoneMsg != NULL) {
+                    mDoneMsg->setInt32("result", UNKNOWN_ERROR);
+                    mDoneMsg->post();
+                    mDoneMsg = NULL;
+                }
                 break;
             }
 
@@ -309,6 +337,16 @@
                 size_t trackIndex;
                 CHECK(msg->findSize("track-index", &trackIndex));
 
+                int32_t eos;
+                if (msg->findInt32("eos", &eos)) {
+                    LOG(INFO) << "received BYE on track index " << trackIndex;
+#if 0
+                    TrackInfo *track = &mTracks.editItemAt(trackIndex);
+                    track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
+#endif
+                    return;
+                }
+
                 sp<RefBase> obj;
                 CHECK(msg->findObject("access-unit", &obj));
 
@@ -318,7 +356,20 @@
                 CHECK(accessUnit->meta()->findInt64(
                             "ntp-time", (int64_t *)&ntpTime));
 
-                accessUnit->meta()->setInt64("ntp-time", ntpTime);
+                if (mFirstAccessUnit) {
+                    mFirstAccessUnit = false;
+                    mFirstAccessUnitNTP = ntpTime;
+                }
+
+                if (ntpTime >= mFirstAccessUnitNTP) {
+                    ntpTime -= mFirstAccessUnitNTP;
+                } else {
+                    ntpTime = 0;
+                }
+
+                int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+
+                accessUnit->meta()->setInt64("timeUs", timeUs);
 
 #if 0
                 int32_t damaged;
@@ -334,6 +385,107 @@
                 break;
             }
 
+            case 'seek':
+            {
+                if (mSeekPending) {
+                    break;
+                }
+
+                int64_t timeUs;
+                CHECK(msg->findInt64("time", &timeUs));
+
+                mSeekPending = true;
+
+                AString request = "PAUSE ";
+                request.append(mSessionURL);
+                request.append(" RTSP/1.0\r\n");
+
+                request.append("Session: ");
+                request.append(mSessionID);
+                request.append("\r\n");
+
+                request.append("\r\n");
+
+                sp<AMessage> reply = new AMessage('see1', id());
+                reply->setInt64("time", timeUs);
+                mConn->sendRequest(request.c_str(), reply);
+                break;
+            }
+
+            case 'see1':
+            {
+                int64_t timeUs;
+                CHECK(msg->findInt64("time", &timeUs));
+
+                AString request = "PLAY ";
+                request.append(mSessionURL);
+                request.append(" RTSP/1.0\r\n");
+
+                request.append("Session: ");
+                request.append(mSessionID);
+                request.append("\r\n");
+
+                request.append(
+                        StringPrintf(
+                            "Range: npt=%lld-\r\n", timeUs / 1000000ll));
+
+                request.append("\r\n");
+
+                sp<AMessage> reply = new AMessage('see2', id());
+                mConn->sendRequest(request.c_str(), reply);
+                break;
+            }
+
+            case 'see2':
+            {
+                CHECK(mSeekPending);
+
+                LOG(INFO) << "seek completed.";
+                mSeekPending = false;
+
+                int32_t result;
+                CHECK(msg->findInt32("result", &result));
+                if (result != OK) {
+                    LOG(ERROR) << "seek FAILED";
+                    break;
+                }
+
+                sp<RefBase> obj;
+                CHECK(msg->findObject("response", &obj));
+                sp<ARTSPResponse> response =
+                    static_cast<ARTSPResponse *>(obj.get());
+
+                CHECK_EQ(response->mStatusCode, 200u);
+
+                for (size_t i = 0; i < mTracks.size(); ++i) {
+                    mTracks.editItemAt(i).mPacketSource->flushQueue();
+                }
+                break;
+            }
+
+            case 'biny':
+            {
+                sp<RefBase> obj;
+                CHECK(msg->findObject("buffer", &obj));
+                sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+                int32_t index;
+                CHECK(buffer->meta()->findInt32("index", &index));
+
+                mRTPConn->injectPacket(index, buffer);
+                break;
+            }
+
+            case 'tiou':
+            {
+                if (mFirstAccessUnit) {
+                    LOG(WARNING) << "Never received any data, disconnecting.";
+
+                }
+                (new AMessage('abor', id()))->post();
+                break;
+            }
+
             default:
                 TRESPASS();
                 break;
@@ -361,15 +513,21 @@
     AString mBaseURL;
     AString mSessionID;
     bool mSetupTracksSuccessful;
+    bool mSeekPending;
+    bool mFirstAccessUnit;
+    uint64_t mFirstAccessUnitNTP;
 
     struct TrackInfo {
         int mRTPSocket;
         int mRTCPSocket;
+        bool mUsingInterleavedTCP;
 
         sp<APacketSource> mPacketSource;
     };
     Vector<TrackInfo> mTracks;
 
+    sp<AMessage> mDoneMsg;
+
     void setupTrack(size_t index) {
         sp<APacketSource> source =
             new APacketSource(mSessionDesc, index);
@@ -393,19 +551,33 @@
         mTracks.push(TrackInfo());
         TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
         info->mPacketSource = source;
-
-        unsigned rtpPort;
-        ARTPConnection::MakePortPair(
-                &info->mRTPSocket, &info->mRTCPSocket, &rtpPort);
+        info->mUsingInterleavedTCP = false;
 
         AString request = "SETUP ";
         request.append(trackURL);
         request.append(" RTSP/1.0\r\n");
 
+#if USE_TCP_INTERLEAVED
+        size_t interleaveIndex = 2 * (mTracks.size() - 1);
+        info->mUsingInterleavedTCP = true;
+        info->mRTPSocket = interleaveIndex;
+        info->mRTCPSocket = interleaveIndex + 1;
+
+        request.append("Transport: RTP/AVP/TCP;interleaved=");
+        request.append(interleaveIndex);
+        request.append("-");
+        request.append(interleaveIndex + 1);
+#else
+        unsigned rtpPort;
+        ARTPConnection::MakePortPair(
+                &info->mRTPSocket, &info->mRTCPSocket, &rtpPort);
+
         request.append("Transport: RTP/AVP/UDP;unicast;client_port=");
         request.append(rtpPort);
         request.append("-");
         request.append(rtpPort + 1);
+#endif
+
         request.append("\r\n");
 
         if (index > 1) {
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 27dc796..ec78ff0 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -242,6 +242,16 @@
     putUInt32(value[3]);
 }
 
+void MtpDataPacket::putInt128(int64_t value) {
+    putInt64(value);
+    putInt64(value < 0 ? -1 : 0);
+}
+
+void MtpDataPacket::putUInt128(uint64_t value) {
+    putUInt64(value);
+    putUInt64(0);
+}
+
 void MtpDataPacket::putAInt8(const int8_t* values, int count) {
     putUInt32(count);
     for (int i = 0; i < count; i++)
@@ -363,7 +373,7 @@
 int MtpDataPacket::write(int fd) {
     MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
     MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
-
+    dump();
     // send header separately from data
     int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
     if (ret == MTP_CONTAINER_HEADER_SIZE)
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index 1467aab..fab6a07 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -69,6 +69,8 @@
     void                putUInt64(uint64_t value);
     void                putInt128(const int128_t& value);
     void                putUInt128(const uint128_t& value);
+    void                putInt128(int64_t value);
+    void                putUInt128(uint64_t value);
 
     void                putAInt8(const int8_t* values, int count);
     void                putAUInt8(const uint8_t* values, int count);
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 17823df..899b34a 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -22,6 +22,7 @@
 namespace android {
 
 class MtpDataPacket;
+class MtpProperty;
 
 class MtpDatabase {
 public:
@@ -58,10 +59,22 @@
     virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format) = 0;
     virtual MtpDevicePropertyList*  getSupportedDeviceProperties() = 0;
 
-    virtual MtpResponseCode         getObjectProperty(MtpObjectHandle handle,
+    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
                                             MtpObjectProperty property,
                                             MtpDataPacket& packet) = 0;
 
+    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property) = 0;
+
     virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
                                             MtpDataPacket& packet) = 0;
 
@@ -76,6 +89,10 @@
     virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
                                             MtpObjectHandleList* references) = 0;
 
+    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format) = 0;
+
+    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property) = 0;
 };
 
 }; // namespace android
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 30abfb8..1e64e4f 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -56,17 +56,17 @@
 //    MTP_OPERATION_SET_OBJECT_PROTECTION,
 //    MTP_OPERATION_POWER_DOWN,
 //    MTP_OPERATION_GET_DEVICE_PROP_DESC,
-//    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
-//    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
-//    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
+    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
+    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
+    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
 //    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
 //    MTP_OPERATION_MOVE_OBJECT,
 //    MTP_OPERATION_COPY_OBJECT,
 //    MTP_OPERATION_GET_PARTIAL_OBJECT,
 //    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
     MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
-//    MTP_OPERATION_GET_OBJECT_PROP_DESC,
-    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
+    MTP_OPERATION_GET_OBJECT_PROP_DESC,
+//    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
 //    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
     MTP_OPERATION_GET_OBJECT_REFERENCES,
     MTP_OPERATION_SET_OBJECT_REFERENCES,
@@ -91,7 +91,6 @@
         mSendObjectFormat(0),
         mSendObjectFileSize(0)
 {
-    initObjectProperties();
 }
 
 MtpServer::~MtpServer() {
@@ -136,7 +135,9 @@
 
         // FIXME need to generalize this
         bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
-                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES);
+                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
+                    || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
+                    || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
         if (dataIn) {
             int ret = mData.read(fd);
             if (ret < 0) {
@@ -158,7 +159,6 @@
                 mData.setOperationCode(operation);
                 mData.setTransactionID(transaction);
                 LOGV("sending data:");
-                mData.dump();
                 ret = mData.write(fd);
                 if (ret < 0) {
                     LOGE("request write returned %d, errno: %d", ret, errno);
@@ -187,24 +187,6 @@
     }
 }
 
-MtpProperty* MtpServer::getObjectProperty(MtpPropertyCode propCode) {
-    for (int i = 0; i < mObjectProperties.size(); i++) {
-        MtpProperty* property = mObjectProperties[i];
-        if (property->getPropertyCode() == propCode)
-            return property;
-    }
-    return NULL;
-}
-
-MtpProperty* MtpServer::getDeviceProperty(MtpPropertyCode propCode) {
-    for (int i = 0; i < mDeviceProperties.size(); i++) {
-        MtpProperty* property = mDeviceProperties[i];
-        if (property->getPropertyCode() == propCode)
-            return property;
-    }
-    return NULL;
-}
-
 void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
     if (mSessionOpen) {
         LOGD("sendObjectAdded %d\n", handle);
@@ -227,14 +209,6 @@
     }
 }
 
-void MtpServer::initObjectProperties() {
-    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32));
-    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
-    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64));
-    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR));
-    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32));
-}
-
 bool MtpServer::handleRequest() {
     MtpOperationCode operation = mRequest.getOperationCode();
     MtpResponseCode response;
@@ -281,6 +255,18 @@
         case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
             response = doGetObjectPropValue();
             break;
+        case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
+            response = doSetObjectPropValue();
+            break;
+        case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
+            response = doGetDevicePropValue();
+            break;
+        case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
+            response = doSetDevicePropValue();
+            break;
+        case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
+            response = doResetDevicePropValue();
+            break;
         case MTP_OPERATION_GET_OBJECT_INFO:
             response = doGetObjectInfo();
             break;
@@ -456,13 +442,15 @@
     if (!mSessionOpen)
         return MTP_RESPONSE_SESSION_NOT_OPEN;
     MtpStorageID handle = mRequest.getParameter(1);
+
+    // FIXME - check for invalid object handle
     MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
-    if (!handles) {
+    if (handles) {
+        mData.putAUInt32(handles);
+        delete handles;
+    } else {
         mData.putEmptyArray();
-        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     }
-    mData.putAUInt32(handles);
-    delete handles;
     return MTP_RESPONSE_OK;
 }
 
@@ -479,8 +467,43 @@
 MtpResponseCode MtpServer::doGetObjectPropValue() {
     MtpObjectHandle handle = mRequest.getParameter(1);
     MtpObjectProperty property = mRequest.getParameter(2);
+    LOGD("GetObjectPropValue %d %s\n", handle,
+            MtpDebug::getObjectPropCodeName(property));
 
-    return mDatabase->getObjectProperty(handle, property, mData);
+    return mDatabase->getObjectPropertyValue(handle, property, mData);
+}
+
+MtpResponseCode MtpServer::doSetObjectPropValue() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    MtpObjectProperty property = mRequest.getParameter(2);
+    LOGD("SetObjectPropValue %d %s\n", handle,
+            MtpDebug::getObjectPropCodeName(property));
+
+    return mDatabase->setObjectPropertyValue(handle, property, mData);
+}
+
+MtpResponseCode MtpServer::doGetDevicePropValue() {
+    MtpDeviceProperty property = mRequest.getParameter(1);
+    LOGD("GetDevicePropValue %s\n",
+            MtpDebug::getDevicePropCodeName(property));
+
+    return mDatabase->getDevicePropertyValue(property, mData);
+}
+
+MtpResponseCode MtpServer::doSetDevicePropValue() {
+    MtpDeviceProperty property = mRequest.getParameter(1);
+    LOGD("SetDevicePropValue %s\n",
+            MtpDebug::getDevicePropCodeName(property));
+
+    return mDatabase->setDevicePropertyValue(property, mData);
+}
+
+MtpResponseCode MtpServer::doResetDevicePropValue() {
+    MtpDeviceProperty property = mRequest.getParameter(1);
+    LOGD("ResetDevicePropValue %s\n",
+            MtpDebug::getDevicePropCodeName(property));
+
+    return mDatabase->resetDeviceProperty(property);
 }
 
 MtpResponseCode MtpServer::doGetObjectInfo() {
@@ -592,7 +615,7 @@
     }
 
     mResponse.setParameter(1, storageID);
-    mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent));
+    mResponse.setParameter(2, parent);
     mResponse.setParameter(3, handle);
 
     return MTP_RESPONSE_OK;
@@ -677,11 +700,24 @@
 MtpResponseCode MtpServer::doGetObjectPropDesc() {
     MtpObjectProperty propCode = mRequest.getParameter(1);
     MtpObjectFormat format = mRequest.getParameter(2);
-    MtpProperty* property = getObjectProperty(propCode);
+    LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
+                                        MtpDebug::getFormatCodeName(format));
+    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
     if (!property)
         return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
-
     property->write(mData);
+    delete property;
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetDevicePropDesc() {
+    MtpDeviceProperty propCode = mRequest.getParameter(1);
+    LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
+    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
+    if (!property)
+        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+    property->write(mData);
+    delete property;
     return MTP_RESPONSE_OK;
 }
 
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index 19ccf24..68a6564 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -28,7 +28,6 @@
 namespace android {
 
 class MtpDatabase;
-class MtpProperty;
 class MtpStorage;
 
 class MtpServer {
@@ -57,9 +56,6 @@
 
     MtpStorageList      mStorages;
 
-    MtpPropertyList     mObjectProperties;
-    MtpPropertyList     mDeviceProperties;
-
     // handle for new object, set by SendObjectInfo and used by SendObject
     MtpObjectHandle     mSendObjectHandle;
     MtpObjectFormat     mSendObjectFormat;
@@ -76,15 +72,10 @@
     MtpStorage*         getStorage(MtpStorageID id);
     void                run();
 
-    MtpProperty*        getObjectProperty(MtpPropertyCode propCode);
-    MtpProperty*        getDeviceProperty(MtpPropertyCode propCode);
-
     void                sendObjectAdded(MtpObjectHandle handle);
     void                sendObjectRemoved(MtpObjectHandle handle);
 
 private:
-    void                initObjectProperties();
-
     bool                handleRequest();
 
     MtpResponseCode     doGetDeviceInfo();
@@ -98,12 +89,17 @@
     MtpResponseCode     doGetObjectReferences();
     MtpResponseCode     doSetObjectReferences();
     MtpResponseCode     doGetObjectPropValue();
+    MtpResponseCode     doSetObjectPropValue();
+    MtpResponseCode     doGetDevicePropValue();
+    MtpResponseCode     doSetDevicePropValue();
+    MtpResponseCode     doResetDevicePropValue();
     MtpResponseCode     doGetObjectInfo();
     MtpResponseCode     doGetObject();
     MtpResponseCode     doSendObjectInfo();
     MtpResponseCode     doSendObject();
     MtpResponseCode     doDeleteObject();
     MtpResponseCode     doGetObjectPropDesc();
+    MtpResponseCode     doGetDevicePropDesc();
 };
 
 }; // namespace android
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
index c7f461e..46135ff 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
@@ -27,7 +27,9 @@
 import com.android.mediaframeworktest.functional.MediaAudioManagerTest;
 import com.android.mediaframeworktest.functional.MediaAudioEffectTest;
 import com.android.mediaframeworktest.functional.MediaBassBoostTest;
+import com.android.mediaframeworktest.functional.MediaEnvReverbTest;
 import com.android.mediaframeworktest.functional.MediaEqualizerTest;
+import com.android.mediaframeworktest.functional.MediaPresetReverbTest;
 import com.android.mediaframeworktest.functional.MediaVirtualizerTest;
 import com.android.mediaframeworktest.functional.MediaVisualizerTest;
 import junit.framework.TestSuite;
@@ -62,7 +64,9 @@
         suite.addTestSuite(MediaAudioManagerTest.class);
         suite.addTestSuite(MediaAudioEffectTest.class);
         suite.addTestSuite(MediaBassBoostTest.class);
+        suite.addTestSuite(MediaEnvReverbTest.class);
         suite.addTestSuite(MediaEqualizerTest.class);
+        suite.addTestSuite(MediaPresetReverbTest.class);
         suite.addTestSuite(MediaVirtualizerTest.class);
         suite.addTestSuite(MediaVisualizerTest.class);
         return suite;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java
new file mode 100644
index 0000000..d339e06
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import android.media.Visualizer;
+import android.util.Log;
+
+/**
+ * The EnergyProbe class provides audio signal energy measurements based on the FFT returned
+ * by the Visualizer class. The measure is qualitative and not quantitative in that the returned
+ * value has no unit and is just proportional to the amount of energy present around the
+ * specified frequency.
+ */
+
+public class EnergyProbe {
+    private String TAG = "EnergyProbe";
+
+    private static int CAPTURE_SIZE = 1024;
+    private static int MEASURE_COUNT = 5;
+    private static int AVERAGE_COUNT = 3;
+
+    private Visualizer mVisualizer = null;
+    private int mMaxFrequency = 0;
+    private int mCapturePeriodMs;
+    private byte[] mFft = new byte[CAPTURE_SIZE];
+
+    public EnergyProbe(int session) {
+        try {
+            mVisualizer = new Visualizer(session);
+            if (mVisualizer != null) {
+                mVisualizer.setCaptureSize(CAPTURE_SIZE);
+                mMaxFrequency = mVisualizer.getSamplingRate() / 2000;
+                mCapturePeriodMs = 1000000 / mVisualizer.getMaxCaptureRate();
+            }
+        } catch (UnsupportedOperationException e) {
+            Log.e(TAG, "Error creating visualizer");
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "Error configuring visualizer");
+        }
+    }
+
+    public int capture(int freq) throws InterruptedException {
+        int energy = 0;
+        int count = 0;
+
+        if (freq > mMaxFrequency) {
+            return 0;
+        }
+
+        if (mVisualizer != null) {
+            try {
+                mVisualizer.setEnabled(true);
+                for (int i = 0; i < MEASURE_COUNT; i++) {
+                    if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+                        if (freq == mMaxFrequency) {
+                            energy += (int)mFft[0] * (int)mFft[0];
+                        } else {
+                            int bin = 2 * (freq * CAPTURE_SIZE / mMaxFrequency / 2);
+                            if (bin < 2) bin = 2;
+                            int tmp = 0;
+                            int j;
+                            for (j = 0;
+                                 (j < AVERAGE_COUNT) && ((bin + 2 * j) < CAPTURE_SIZE);
+                                 j++) {
+                                tmp += (int)mFft[bin + 2 * j] * (int)mFft[bin + 2 * j] +
+                                       (int)mFft[bin + 2 * j + 1] * (int)mFft[bin + 2 * j + 1];
+                            }
+                            // j is always != 0
+                            energy += tmp/j;
+                        }
+                        count++;
+                    }
+                    Thread.sleep(mCapturePeriodMs);
+                }
+                mVisualizer.setEnabled(false);
+            } catch (IllegalStateException e) {
+                Log.e(TAG, "Error capturing audio");
+            }
+        }
+        if (count == 0) {
+            return 0;
+        }
+        return energy/count;
+    }
+
+    public void release() {
+        if (mVisualizer != null) {
+            mVisualizer.release();
+            mVisualizer = null;
+        }
+    }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
index 8a68c5e..aca729e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
@@ -43,7 +43,7 @@
  */
 public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
     private String TAG = "MediaBassBoostTest";
-    private final static int MIN_ENERGY_RATIO_2 = 4;
+    private final static int MIN_ENERGY_RATIO_2 = 3;
     private final static short TEST_STRENGTH = 500;
 
     private BassBoost mBassBoost = null;
@@ -259,52 +259,6 @@
     // private methods
     //----------------------------------
 
-    private class EnergyProbe {
-        Visualizer mVisualizer = null;
-        private byte[] mFft = new byte[1024];
-
-        public EnergyProbe(int session) {
-            mVisualizer = new Visualizer(session);
-            mVisualizer.setCaptureSize(1024);
-        }
-
-        public int capture(int freq) throws InterruptedException {
-            int energy = 0;
-            int count = 0;
-            if (mVisualizer != null) {
-                mVisualizer.setEnabled(true);
-                for (int i = 0; i < 10; i++) {
-                    if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
-                        // TODO: check speex FFT as it seems to return only the number of points
-                        // correspondong to valid part of the spectrum (< Fs).
-                        // e.g., if the number of points is 1024, it covers the frequency range
-                        // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
-                        int bin = freq / (22050 / 1024);
-                        int tmp = 0;
-                        for (int j = bin-2; j < bin+3; j++) {
-                            tmp += (int)mFft[j] * (int)mFft[j];
-                        }
-                        energy += tmp/5;
-                        count++;
-                    }
-                    Thread.sleep(50);
-                }
-                mVisualizer.setEnabled(false);
-            }
-            if (count == 0) {
-                return 0;
-            }
-            return energy/count;
-        }
-
-        public void release() {
-            if (mVisualizer != null) {
-                mVisualizer.release();
-                mVisualizer = null;
-            }
-        }
-    }
-
     private void getBassBoost(int session) {
          if (mBassBoost == null || session != mSession) {
              if (session != mSession && mBassBoost != null) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
new file mode 100644
index 0000000..db0db70
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.EnvironmentalReverb;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaEnvReverbTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+    private String TAG = "MediaEnvReverbTest";
+    // allow +/- 100 millibel difference between set and get gains
+    private final static int MILLIBEL_TOLERANCE = 100;
+    // allow +/- 5% tolerance between set and get delays
+    private final static float DELAY_TOLERANCE = 1.05f;
+    // allow +/- 5% tolerance between set and get ratios
+    private final static float RATIO_TOLERANCE = 1.05f;
+
+    private EnvironmentalReverb mReverb = null;
+    private int mSession = -1;
+
+    public MediaEnvReverbTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+      super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        releaseReverb();
+    }
+
+    private static void assumeTrue(String message, boolean cond) {
+        assertTrue("(assume)"+message, cond);
+    }
+
+    private void log(String testName, String message) {
+        Log.v(TAG, "["+testName+"] "+message);
+    }
+
+    private void loge(String testName, String message) {
+        Log.e(TAG, "["+testName+"] "+message);
+    }
+
+    //-----------------------------------------------------------------
+    // ENVIRONMENTAL REVEB TESTS:
+    //----------------------------------
+
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    @LargeTest
+    public void test0_0ConstructorAndRelease() throws Exception {
+        boolean result = false;
+        String msg = "test1_0ConstructorAndRelease()";
+        EnvironmentalReverb reverb = null;
+         try {
+            reverb = new EnvironmentalReverb(0, 0);
+            assertNotNull(msg + ": could not create EnvironmentalReverb", reverb);
+            try {
+                assertTrue(msg +": invalid effect ID", (reverb.getId() != 0));
+            } catch (IllegalStateException e) {
+                msg = msg.concat(": EnvironmentalReverb not initialized");
+            }
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": EnvironmentalReverb not found");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": Effect library not loaded");
+        } finally {
+            if (reverb != null) {
+                reverb.release();
+            }
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test room level and room HF level
+    @LargeTest
+    public void test1_0Room() throws Exception {
+        boolean result = false;
+        String msg = "test1_0Room()";
+        getReverb(0);
+        try {
+            mReverb.setRoomLevel((short)0);
+            short level = mReverb.getRoomLevel();
+            assertTrue(msg +": got incorrect room level",
+                    (level > (0 - MILLIBEL_TOLERANCE)) &&
+                    (level < (0 + MILLIBEL_TOLERANCE)));
+
+            mReverb.setRoomHFLevel((short)-6);
+            level = mReverb.getRoomHFLevel();
+            assertTrue(msg +": got incorrect room HF level",
+                    (level > (-6 - MILLIBEL_TOLERANCE)) &&
+                    (level < (-6 + MILLIBEL_TOLERANCE)));
+
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 1.1: test decay time and ratio
+    @LargeTest
+    public void test1_1Decay() throws Exception {
+        boolean result = false;
+        String msg = "test1_1Decay()";
+        getReverb(0);
+        try {
+            mReverb.setDecayTime(500);
+            int time = mReverb.getDecayTime();
+            assertTrue(msg +": got incorrect decay time",
+                    ((float)time > (float)(500 / DELAY_TOLERANCE)) &&
+                    ((float)time < (float)(500 * DELAY_TOLERANCE)));
+
+            mReverb.setDecayHFRatio((short)1000);
+            short ratio = mReverb.getDecayHFRatio();
+            assertTrue(msg +": got incorrect decay HF ratio",
+                    ((float)ratio > (float)(1000 / RATIO_TOLERANCE)) &&
+                    ((float)ratio < (float)(1000 * RATIO_TOLERANCE)));
+
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 1.2: test reflections
+    @LargeTest
+    public void test1_2Reflections() throws Exception {
+        // TODO: uncomment when early reflections are implemented
+//        boolean result = false;
+//        String msg = "test1_2Reflections()";
+//        getReverb(0);
+//        try {
+//            mReverb.setReflectionsLevel((short)0);
+//            short level = mReverb.getReflectionsLevel();
+//            assertTrue(msg +": got incorrect reflections level",
+//                    (level > (0 - MILLIBEL_TOLERANCE)) &&
+//                    (level < (0 + MILLIBEL_TOLERANCE)));
+//
+//            mReverb.setReflectionsDelay(30);
+//            int delay = mReverb.getReflectionsDelay();
+//            assertTrue(msg +": got incorrect reflections delay",
+//                    ((float)delay > (float)(30 / DELAY_TOLERANCE)) &&
+//                    ((float)delay < (float)(30 * DELAY_TOLERANCE)));
+//
+//            result = true;
+//        } catch (IllegalArgumentException e) {
+//            msg = msg.concat(": Bad parameter value");
+//            loge(msg, "Bad parameter value");
+//        } catch (UnsupportedOperationException e) {
+//            msg = msg.concat(": get parameter() rejected");
+//            loge(msg, "get parameter() rejected");
+//        } catch (IllegalStateException e) {
+//            msg = msg.concat("get parameter() called in wrong state");
+//            loge(msg, "get parameter() called in wrong state");
+//        } finally {
+//            releaseReverb();
+//        }
+//        assertTrue(msg, result);
+    }
+
+    //Test case 1.3: test reverb
+    @LargeTest
+    public void test1_3Reverb() throws Exception {
+        boolean result = false;
+        String msg = "test1_3Reverb()";
+        getReverb(0);
+        try {
+            mReverb.setReverbLevel((short)0);
+            short level = mReverb.getReverbLevel();
+            assertTrue(msg +": got incorrect reverb level",
+                    (level > (0 - MILLIBEL_TOLERANCE)) &&
+                    (level < (0 + MILLIBEL_TOLERANCE)));
+
+            // TODO: change delay when early reflections are implemented
+            mReverb.setReverbDelay(0);
+            int delay = mReverb.getReverbDelay();
+            assertTrue(msg +": got incorrect reverb delay", delay < 5);
+
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 1.4: test diffusion and density
+    @LargeTest
+    public void test1_4DiffusionAndDensity() throws Exception {
+        boolean result = false;
+        String msg = "test1_4DiffusionAndDensity()";
+        getReverb(0);
+        try {
+            mReverb.setDiffusion((short)500);
+            short diffusion = mReverb.getDiffusion();
+            assertTrue(msg +": got incorrect diffusion",
+                    ((float)diffusion > (float)(500 / RATIO_TOLERANCE)) &&
+                    ((float)diffusion < (float)(500 * RATIO_TOLERANCE)));
+
+            mReverb.setDensity((short)500);
+            short density = mReverb.getDensity();
+            assertTrue(msg +": got incorrect density",
+                    ((float)density > (float)(500 / RATIO_TOLERANCE)) &&
+                    ((float)density < (float)(500 * RATIO_TOLERANCE)));
+
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 1.5: test properties
+    @LargeTest
+    public void test1_5Properties() throws Exception {
+        boolean result = false;
+        String msg = "test1_5Properties()";
+        getReverb(0);
+        try {
+            EnvironmentalReverb.Settings settings = mReverb.getProperties();
+            short newRoomLevel = 0;
+            if (settings.roomLevel == 0) {
+                newRoomLevel = -1000;
+            }
+            String str = settings.toString();
+            settings = new EnvironmentalReverb.Settings(str);
+            settings.roomLevel = newRoomLevel;
+            mReverb.setProperties(settings);
+            settings = mReverb.getProperties();
+            assertTrue(msg +": setProperties failed",
+                    (settings.roomLevel > (newRoomLevel - MILLIBEL_TOLERANCE)) &&
+                    (settings.roomLevel < (newRoomLevel + MILLIBEL_TOLERANCE)));
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - Effect action
+    //----------------------------------
+
+    //Test case 2.0: test actual auxiliary reverb influence on sound
+    @LargeTest
+    public void test2_0AuxiliarySoundModification() throws Exception {
+        boolean result = false;
+        String msg = "test2_0AuxiliarySoundModification()";
+        EnergyProbe probe = null;
+        AudioEffect vc = null;
+        MediaPlayer mp = null;
+        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           0);
+        getReverb(0);
+        try {
+            probe = new EnergyProbe(0);
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+                      0,
+                      0);
+            vc.setEnabled(true);
+
+            mp = new MediaPlayer();
+            mp.setDataSource(MediaNames.SINE_200_1000);
+            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mp.attachAuxEffect(mReverb.getId());
+            mp.setAuxEffectSendLevel(1.0f);
+            mReverb.setRoomLevel((short)0);
+            mReverb.setReverbLevel((short)0);
+            mReverb.setDecayTime(2000);
+            mReverb.setEnabled(true);
+            mp.prepare();
+            mp.start();
+            Thread.sleep(1000);
+            mp.stop();
+            Thread.sleep(300);
+            // measure energy around 1kHz after media player was stopped for 300 ms
+            int energy1000 = probe.capture(1000);
+            assertTrue(msg + ": reverb has no effect", energy1000 > 0);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } catch (InterruptedException e) {
+            loge(msg, "sleep() interrupted");
+        }
+        finally {
+            releaseReverb();
+            if (mp != null) {
+                mp.release();
+            }
+            if (vc != null) {
+                vc.release();
+            }
+            if (probe != null) {
+                probe.release();
+            }
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 2.1: test actual insert reverb influence on sound
+    @LargeTest
+    public void test2_1InsertSoundModification() throws Exception {
+        boolean result = false;
+        String msg = "test2_1InsertSoundModification()";
+        EnergyProbe probe = null;
+        AudioEffect vc = null;
+        MediaPlayer mp = null;
+        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           0);
+        try {
+            probe = new EnergyProbe(0);
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+                      0,
+                      0);
+            vc.setEnabled(true);
+
+            mp = new MediaPlayer();
+            mp.setDataSource(MediaNames.SINE_200_1000);
+            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            getReverb(mp.getAudioSessionId());
+            mReverb.setRoomLevel((short)0);
+            mReverb.setReverbLevel((short)0);
+            mReverb.setDecayTime(2000);
+            mReverb.setEnabled(true);
+            mp.prepare();
+            mp.start();
+            Thread.sleep(1000);
+            mp.stop();
+            Thread.sleep(300);
+            // measure energy around 1kHz after media player was stopped for 300 ms
+            int energy1000 = probe.capture(1000);
+            assertTrue(msg + ": reverb has no effect", energy1000 > 0);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } catch (InterruptedException e) {
+            loge(msg, "sleep() interrupted");
+        }
+        finally {
+            releaseReverb();
+            if (mp != null) {
+                mp.release();
+            }
+            if (vc != null) {
+                vc.release();
+            }
+            if (probe != null) {
+                probe.release();
+            }
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private void getReverb(int session) {
+         if (mReverb == null || session != mSession) {
+             if (session != mSession && mReverb != null) {
+                 mReverb.release();
+                 mReverb = null;
+             }
+             try {
+                mReverb = new EnvironmentalReverb(0, session);
+                mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getReverb() EnvironmentalReverb not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getReverb() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mReverb", mReverb);
+    }
+
+    private void releaseReverb() {
+        if (mReverb != null) {
+            mReverb.release();
+            mReverb = null;
+        }
+   }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
index e46887b..7b3945d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
@@ -322,52 +322,6 @@
     // private methods
     //----------------------------------
 
-    private class EnergyProbe {
-        Visualizer mVisualizer = null;
-        private byte[] mFft = new byte[1024];
-
-        public EnergyProbe(int session) {
-            mVisualizer = new Visualizer(session);
-            mVisualizer.setCaptureSize(1024);
-        }
-
-        public int capture(int freq) throws InterruptedException {
-            int energy = 0;
-            int count = 0;
-            if (mVisualizer != null) {
-                mVisualizer.setEnabled(true);
-                for (int i = 0; i < 10; i++) {
-                    if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
-                        // TODO: check speex FFT as it seems to return only the number of points
-                        // correspondong to valid part of the spectrum (< Fs).
-                        // e.g., if the number of points is 1024, it covers the frequency range
-                        // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
-                        int bin = freq / (22050 / 1024);
-                        int tmp = 0;
-                        for (int j = bin-2; j < bin+3; j++) {
-                            tmp += (int)mFft[j] * (int)mFft[j];
-                        }
-                        energy += tmp/5;
-                        count++;
-                    }
-                    Thread.sleep(50);
-                }
-                mVisualizer.setEnabled(false);
-            }
-            if (count == 0) {
-                return 0;
-            }
-            return energy/count;
-        }
-
-        public void release() {
-            if (mVisualizer != null) {
-                mVisualizer.release();
-                mVisualizer = null;
-            }
-        }
-    }
-
     private void getEqualizer(int session) {
          if (mEqualizer == null || session != mSession) {
              if (session != mSession && mEqualizer != null) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
new file mode 100644
index 0000000..c14319a
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.PresetReverb;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaPresetReverbTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+    private String TAG = "MediaPresetReverbTest";
+
+    private PresetReverb mReverb = null;
+    private int mSession = -1;
+
+    public MediaPresetReverbTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+      super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        releaseReverb();
+    }
+
+    private static void assumeTrue(String message, boolean cond) {
+        assertTrue("(assume)"+message, cond);
+    }
+
+    private void log(String testName, String message) {
+        Log.v(TAG, "["+testName+"] "+message);
+    }
+
+    private void loge(String testName, String message) {
+        Log.e(TAG, "["+testName+"] "+message);
+    }
+
+    //-----------------------------------------------------------------
+    // PRESET REVEB TESTS:
+    //----------------------------------
+
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    @LargeTest
+    public void test0_0ConstructorAndRelease() throws Exception {
+        boolean result = false;
+        String msg = "test1_0ConstructorAndRelease()";
+        PresetReverb reverb = null;
+         try {
+            reverb = new PresetReverb(0, 0);
+            assertNotNull(msg + ": could not create PresetReverb", reverb);
+            try {
+                assertTrue(msg +": invalid effect ID", (reverb.getId() != 0));
+            } catch (IllegalStateException e) {
+                msg = msg.concat(": PresetReverb not initialized");
+            }
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": PresetReverb not found");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": Effect library not loaded");
+        } finally {
+            if (reverb != null) {
+                reverb.release();
+            }
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test preset
+    @LargeTest
+    public void test1_0Preset() throws Exception {
+        boolean result = false;
+        String msg = "test1_0Preset()";
+        getReverb(0);
+        try {
+            mReverb.setPreset((short)PresetReverb.PRESET_LARGEROOM);
+            short preset = mReverb.getPreset();
+            assertEquals(msg +": got incorrect preset",
+                         (short)PresetReverb.PRESET_LARGEROOM,
+                         preset);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 1.1: test properties
+    @LargeTest
+    public void test1_1Properties() throws Exception {
+        boolean result = false;
+        String msg = "test1_1Properties()";
+        getReverb(0);
+        try {
+            PresetReverb.Settings settings = mReverb.getProperties();
+            short newPreset = (short)PresetReverb.PRESET_LARGEROOM;
+            if (settings.preset == (short)PresetReverb.PRESET_LARGEROOM) {
+                newPreset = (short)PresetReverb.PRESET_SMALLROOM;
+            }
+            String str = settings.toString();
+            settings = new PresetReverb.Settings(str);
+            settings.preset = newPreset;
+            mReverb.setProperties(settings);
+            settings = mReverb.getProperties();
+            assertEquals(msg +": setProperties failed", newPreset, settings.preset);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - Effect action
+    //----------------------------------
+
+    //Test case 2.0: test actual auxiliary reverb influence on sound
+    @LargeTest
+    public void test2_0AuxiliarySoundModification() throws Exception {
+        boolean result = false;
+        String msg = "test2_0AuxiliarySoundModification()";
+        EnergyProbe probe = null;
+        AudioEffect vc = null;
+        MediaPlayer mp = null;
+        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           0);
+        getReverb(0);
+        try {
+            probe = new EnergyProbe(0);
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+                      0,
+                      0);
+            vc.setEnabled(true);
+
+            mp = new MediaPlayer();
+            mp.setDataSource(MediaNames.SINE_200_1000);
+            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mp.attachAuxEffect(mReverb.getId());
+            mp.setAuxEffectSendLevel(1.0f);
+            mReverb.setPreset((short)PresetReverb.PRESET_PLATE);
+            mReverb.setEnabled(true);
+            mp.prepare();
+            mp.start();
+            Thread.sleep(1000);
+            mp.stop();
+            Thread.sleep(200);
+            // measure energy around 1kHz after media player was stopped for 200 ms
+            int energy1000 = probe.capture(1000);
+            assertTrue(msg + ": reverb has no effect", energy1000 > 0);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } catch (InterruptedException e) {
+            loge(msg, "sleep() interrupted");
+        }
+        finally {
+            releaseReverb();
+            if (mp != null) {
+                mp.release();
+            }
+            if (vc != null) {
+                vc.release();
+            }
+            if (probe != null) {
+                probe.release();
+            }
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 2.1: test actual insert reverb influence on sound
+    @LargeTest
+    public void test2_1InsertSoundModification() throws Exception {
+        boolean result = false;
+        String msg = "test2_1InsertSoundModification()";
+        EnergyProbe probe = null;
+        AudioEffect vc = null;
+        MediaPlayer mp = null;
+        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           0);
+        try {
+            probe = new EnergyProbe(0);
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+                      0,
+                      0);
+            vc.setEnabled(true);
+
+            mp = new MediaPlayer();
+            mp.setDataSource(MediaNames.SINE_200_1000);
+            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            getReverb(mp.getAudioSessionId());
+            mReverb.setPreset((short)PresetReverb.PRESET_PLATE);
+            mReverb.setEnabled(true);
+            mp.prepare();
+            mp.start();
+            Thread.sleep(1000);
+            mp.stop();
+            Thread.sleep(200);
+            // measure energy around 1kHz after media player was stopped for 200 ms
+            int energy1000 = probe.capture(1000);
+            assertTrue(msg + ": reverb has no effect", energy1000 > 0);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } catch (InterruptedException e) {
+            loge(msg, "sleep() interrupted");
+        }
+        finally {
+            releaseReverb();
+            if (mp != null) {
+                mp.release();
+            }
+            if (vc != null) {
+                vc.release();
+            }
+            if (probe != null) {
+                probe.release();
+            }
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private void getReverb(int session) {
+         if (mReverb == null || session != mSession) {
+             if (session != mSession && mReverb != null) {
+                 mReverb.release();
+                 mReverb = null;
+             }
+             try {
+                mReverb = new PresetReverb(0, session);
+                mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getReverb() PresetReverb not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getReverb() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mReverb", mReverb);
+    }
+
+    private void releaseReverb() {
+        if (mReverb != null) {
+            mReverb.release();
+            mReverb = null;
+        }
+   }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
index 6b8ae44..517d575 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
@@ -43,7 +43,7 @@
  */
 public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
     private String TAG = "MediaVirtualizerTest";
-    private final static int MIN_ENERGY_RATIO_2 = 4;
+    private final static int MIN_ENERGY_RATIO_2 = 3;
     private final static short TEST_STRENGTH = 500;
 
     private Virtualizer mVirtualizer = null;
@@ -224,7 +224,7 @@
             int energy200 = probe.capture(200);
             int energy1000 = probe.capture(1000);
             // verify that the energy ration between low and high frequencies is at least
-            // four times higher with virtualizer on.
+            // MIN_ENERGY_RATIO_2 times higher with virtualizer on.
             // NOTE: this is what is observed with current virtualizer implementation and the test
             // audio file but is not the primary effect of the virtualizer. A better way would
             // be to have a stereo PCM capture and check that a strongly paned input is centered
@@ -264,52 +264,6 @@
     // private methods
     //----------------------------------
 
-    private class EnergyProbe {
-        Visualizer mVisualizer = null;
-        private byte[] mFft = new byte[1024];
-
-        public EnergyProbe(int session) {
-            mVisualizer = new Visualizer(session);
-            mVisualizer.setCaptureSize(1024);
-        }
-
-        public int capture(int freq) throws InterruptedException {
-            int energy = 0;
-            int count = 0;
-            if (mVisualizer != null) {
-                mVisualizer.setEnabled(true);
-                for (int i = 0; i < 10; i++) {
-                    if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
-                        // TODO: check speex FFT as it seems to return only the number of points
-                        // correspondong to valid part of the spectrum (< Fs).
-                        // e.g., if the number of points is 1024, it covers the frequency range
-                        // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
-                        int bin = freq / (22050 / 1024);
-                        int tmp = 0;
-                        for (int j = bin-2; j < bin+3; j++) {
-                            tmp += (int)mFft[j] * (int)mFft[j];
-                        }
-                        energy += tmp/5;
-                        count++;
-                    }
-                    Thread.sleep(50);
-                }
-                mVisualizer.setEnabled(false);
-            }
-            if (count == 0) {
-                return 0;
-            }
-            return energy/count;
-        }
-
-        public void release() {
-            if (mVisualizer != null) {
-                mVisualizer.release();
-                mVisualizer = null;
-            }
-        }
-    }
-
     private void getVirtualizer(int session) {
          if (mVirtualizer == null || session != mSession) {
              if (session != mSession && mVirtualizer != null) {
diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp
index b3cc8b6..53308be 100644
--- a/media/tests/players/invoke_mock_media_player.cpp
+++ b/media/tests/players/invoke_mock_media_player.cpp
@@ -26,6 +26,7 @@
 
 using android::INVALID_OPERATION;
 using android::ISurface;
+using android::Surface;
 using android::MediaPlayerBase;
 using android::OK;
 using android::Parcel;
@@ -67,7 +68,8 @@
     }
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length) {return OK;}
-    virtual status_t    setVideoSurface(const sp<ISurface>& surface) {return OK;}
+    virtual status_t    setVideoISurface(const sp<ISurface>& surface) {return OK;}
+    virtual status_t    setVideoSurface(const sp<Surface>& surface) {return OK;}
     virtual status_t    prepare() {return OK;}
     virtual status_t    prepareAsync() {return OK;}
     virtual status_t    start() {return OK;}
diff --git a/native/android/Android.mk b/native/android/Android.mk
index bd2b27a..cc35a3a 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -12,7 +12,8 @@
     looper.cpp \
     native_activity.cpp \
     native_window.cpp \
-    sensor.cpp
+    sensor.cpp \
+    storage_manager.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
@@ -23,6 +24,9 @@
     libsurfaceflinger_client \
     libandroid_runtime
 
+LOCAL_STATIC_LIBRARIES := \
+    libstorage
+
 LOCAL_C_INCLUDES += \
     frameworks/base/native/include \
     frameworks/base/core/jni/android \
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
new file mode 100644
index 0000000..6dbe746
--- /dev/null
+++ b/native/android/storage_manager.cpp
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "NStorage"
+
+#include <android/storage_manager.h>
+#include <storage/IMountService.h>
+
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+
+using namespace android;
+
+struct ObbActionListener : public BnObbActionListener {
+private:
+    sp<AStorageManager> mStorageManager;
+
+public:
+    ObbActionListener(AStorageManager* mgr) :
+            mStorageManager(mgr)
+    {}
+
+    virtual void onObbResult(const android::String16& filename, const android::String16& state) {
+        LOGD("Got obb result (%s, %s)\n", String8(filename).string(), String8(state).string());
+    }
+};
+
+struct AStorageManager : public RefBase {
+protected:
+    void* mObbCallback;
+    sp<ObbActionListener> mObbActionListener;
+    sp<IMountService> mMountService;
+
+public:
+    AStorageManager() :
+            mObbCallback(NULL)
+    {
+    }
+
+    bool initialize() {
+        sp<IServiceManager> sm = defaultServiceManager();
+        if (sm == NULL) {
+            LOGE("Couldn't get default ServiceManager\n");
+            return false;
+        }
+
+        mMountService = interface_cast<IMountService>(sm->getService(String16("mount")));
+        if (mMountService == NULL) {
+            LOGE("Couldn't get connection to MountService\n");
+            return false;
+        }
+
+        mObbActionListener = new ObbActionListener(this);
+
+        return true;
+    }
+
+    void setObbCallback(void* cb) {
+        mObbCallback = cb;
+    }
+
+    void mountObb(const char* filename, const char* key) {
+        String16 filename16(filename);
+        String16 key16(key);
+        mMountService->mountObb(filename16, key16, mObbActionListener);
+    }
+
+    void unmountObb(const char* filename, const bool force) {
+        String16 filename16(filename);
+        mMountService->unmountObb(filename16, force);
+    }
+
+    int isObbMounted(const char* filename) {
+        String16 filename16(filename);
+        return mMountService->isObbMounted(filename16);
+    }
+
+    const char* getMountedObbPath(const char* filename) {
+        String16 filename16(filename);
+        String16 path16;
+        if (mMountService->getMountedObbPath(filename16, path16)) {
+            return String8(path16).string();
+        } else {
+            return NULL;
+        }
+    }
+};
+
+
+AStorageManager* AStorageManager_new() {
+    sp<AStorageManager> mgr = new AStorageManager();
+    if (mgr == NULL || !mgr->initialize()) {
+        return NULL;
+    }
+    mgr->incStrong((void*)AStorageManager_new);
+    return static_cast<AStorageManager*>(mgr.get());
+}
+
+void AStorageManager_delete(AStorageManager* mgr) {
+    if (mgr) {
+        mgr->decStrong((void*)AStorageManager_new);
+    }
+}
+
+void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb) {
+    mgr->setObbCallback(cb);
+}
+
+void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) {
+    mgr->mountObb(filename, key);
+}
+
+void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force) {
+    mgr->unmountObb(filename, force != 0);
+}
+
+int AStorageManager_isObbMounted(AStorageManager* mgr, const char* filename) {
+    return mgr->isObbMounted(filename) != 0;
+}
+
+const char* AStorageManager_getMountedObbPath(AStorageManager* mgr, const char* filename) {
+    return mgr->getMountedObbPath(filename);
+}
diff --git a/native/include/android/storage_manager.h b/native/include/android/storage_manager.h
new file mode 100644
index 0000000..bbed8a4
--- /dev/null
+++ b/native/include/android/storage_manager.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_STORAGE_MANAGER_H
+#define ANDROID_STORAGE_MANAGER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AStorageManager;
+typedef struct AStorageManager AStorageManager;
+
+
+/**
+ * Obtains a new instance of AStorageManager.
+ */
+AStorageManager* AStorageManager_new();
+
+/**
+ * Release AStorageManager instance.
+ */
+void AStorageManager_delete(AStorageManager* mgr);
+
+/**
+ * Callback to call when requested OBB is complete.
+ */
+void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb);
+
+/**
+ * Attempts to mount an OBB file.
+ */
+void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key);
+
+/**
+ * Attempts to unmount an OBB file.
+ */
+void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force);
+
+/**
+ * Check whether an OBB is mounted.
+ */
+int AStorageManager_isObbMounted(AStorageManager* mgr, const char* filename);
+
+/**
+ * Get the mounted path for an OBB.
+ */
+const char* AStorageManager_getMountedObbPath(AStorageManager* mgr, const char* filename);
+
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif      // ANDROID_PACKAGE_MANAGER_H
diff --git a/core/java/android/content/ClippedData.aidl b/opengl/java/android/opengl/GLWallpaperService.java
similarity index 65%
copy from core/java/android/content/ClippedData.aidl
copy to opengl/java/android/opengl/GLWallpaperService.java
index 5246526..c954fed 100644
--- a/core/java/android/content/ClippedData.aidl
+++ b/opengl/java/android/opengl/GLWallpaperService.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2010, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
-package android.content;
+package android.opengl;
 
-parcelable ClippedData;
+/**
+ * reserve this namespace for future use
+ * (making sure external developers don't use it)
+ * @hide
+ */
+class GLWallpaperService {
+}
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 4997dc8..7fbe9b5 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -685,8 +685,8 @@
             } while (num);
         }
         if (count) {
-            v0 = c->vc.vBuffer + 2 + num - 2;
-            v1 = c->vc.vBuffer + 2 + num - 1;
+            v0 = c->vc.vBuffer + 2 + vcs - 2;
+            v1 = c->vc.vBuffer + 2 + vcs - 1;
             if ((winding&2) == 0) {
                 // for strips copy back the two last compiled vertices
                 c->vc.vBuffer[0] = *v0;
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 8b476c0..53cc398 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -576,41 +576,44 @@
     buffer = 0;
 
     // dequeue a new buffer
-    nativeWindow->dequeueBuffer(nativeWindow, &buffer);
-    
-    // TODO: lockBuffer should rather be executed when the very first
-    // direct rendering occurs.
-    nativeWindow->lockBuffer(nativeWindow, buffer);
-    
-    // reallocate the depth-buffer if needed
-    if ((width != buffer->width) || (height != buffer->height)) {
-        // TODO: we probably should reset the swap rect here
-        // if the window size has changed
-        width = buffer->width;
-        height = buffer->height;
-        if (depth.data) {
-            free(depth.data);
-            depth.width   = width;
-            depth.height  = height;
-            depth.stride  = buffer->stride;
-            depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
-            if (depth.data == 0) {
-                setError(EGL_BAD_ALLOC, EGL_FALSE);
-                return EGL_FALSE;
+    if (nativeWindow->dequeueBuffer(nativeWindow, &buffer) == NO_ERROR) {
+
+        // TODO: lockBuffer should rather be executed when the very first
+        // direct rendering occurs.
+        nativeWindow->lockBuffer(nativeWindow, buffer);
+
+        // reallocate the depth-buffer if needed
+        if ((width != buffer->width) || (height != buffer->height)) {
+            // TODO: we probably should reset the swap rect here
+            // if the window size has changed
+            width = buffer->width;
+            height = buffer->height;
+            if (depth.data) {
+                free(depth.data);
+                depth.width   = width;
+                depth.height  = height;
+                depth.stride  = buffer->stride;
+                depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
+                if (depth.data == 0) {
+                    setError(EGL_BAD_ALLOC, EGL_FALSE);
+                    return EGL_FALSE;
+                }
             }
         }
-    }
-    
-    // keep a reference on the buffer
-    buffer->common.incRef(&buffer->common);
 
-    // finally pin the buffer down
-    if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | 
-            GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
-        LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
-                buffer, buffer->width, buffer->height);
-        return setError(EGL_BAD_ACCESS, EGL_FALSE);
-        // FIXME: we should make sure we're not accessing the buffer anymore
+        // keep a reference on the buffer
+        buffer->common.incRef(&buffer->common);
+
+        // finally pin the buffer down
+        if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
+                GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
+            LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
+                    buffer, buffer->width, buffer->height);
+            return setError(EGL_BAD_ACCESS, EGL_FALSE);
+            // FIXME: we should make sure we're not accessing the buffer anymore
+        }
+    } else {
+        return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE);
     }
 
     return EGL_TRUE;
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 185d72a9..8349fe6 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -47,7 +47,8 @@
     <bool name="def_networks_available_notification_on">true</bool>
     
     <bool name="def_backup_enabled">false</bool>
-    <string name="def_backup_transport" translatable="false"></string>
+    <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string>
+
     <!-- Default value for whether or not to pulse the notification LED when there is a 
          pending notification -->
     <bool name="def_notification_pulse">true</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6a5290e..81d82de 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -22,6 +22,8 @@
 import java.security.SecureRandom;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import android.app.backup.BackupManager;
 import android.content.ContentProvider;
@@ -37,6 +39,7 @@
 import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.FileObserver;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
 import android.provider.DrmStore;
@@ -56,9 +59,15 @@
 
     // Cache for settings, access-ordered for acting as LRU.
     // Guarded by themselves.
-    private static final int MAX_CACHE_ENTRIES = 50;
-    private static final SettingsCache sSystemCache = new SettingsCache();
-    private static final SettingsCache sSecureCache = new SettingsCache();
+    private static final int MAX_CACHE_ENTRIES = 200;
+    private static final SettingsCache sSystemCache = new SettingsCache("system");
+    private static final SettingsCache sSecureCache = new SettingsCache("secure");
+
+    // The count of how many known (handled by SettingsProvider)
+    // database mutations are currently being handled.  Used by
+    // sFileObserver to not reload the database when it's ourselves
+    // modifying it.
+    private static final AtomicInteger sKnownMutationsInFlight = new AtomicInteger(0);
 
     // Over this size we don't reject loading or saving settings but
     // we do consider them broken/malicious and don't keep them in
@@ -67,6 +76,10 @@
 
     private static final Bundle NULL_SETTING = Bundle.forPair("value", null);
 
+    // Used as a sentinel value in an instance equality test when we
+    // want to cache the existence of a key, but not store its value.
+    private static final Bundle TOO_LARGE_TO_CACHE_MARKER = Bundle.forPair("_dummy", null);
+
     protected DatabaseHelper mOpenHelper;
     private BackupManager mBackupManager;
 
@@ -201,6 +214,43 @@
         }
     }
 
+    // FileObserver for external modifications to the database file.
+    // Note that this is for platform developers only with
+    // userdebug/eng builds who should be able to tinker with the
+    // sqlite database out from under the SettingsProvider, which is
+    // normally the exclusive owner of the database.  But we keep this
+    // enabled all the time to minimize development-vs-user
+    // differences in testing.
+    private static SettingsFileObserver sObserverInstance;
+    private class SettingsFileObserver extends FileObserver {
+        private final AtomicBoolean mIsDirty = new AtomicBoolean(false);
+        private final String mPath;
+
+        public SettingsFileObserver(String path) {
+            super(path, FileObserver.CLOSE_WRITE |
+                  FileObserver.CREATE | FileObserver.DELETE |
+                  FileObserver.MOVED_TO | FileObserver.MODIFY);
+            mPath = path;
+        }
+
+        public void onEvent(int event, String path) {
+            int modsInFlight = sKnownMutationsInFlight.get();
+            if (modsInFlight > 0) {
+                // our own modification.
+                return;
+            }
+            Log.d(TAG, "external modification to " + mPath + "; event=" + event);
+            if (!mIsDirty.compareAndSet(false, true)) {
+                // already handled. (we get a few update events
+                // during an sqlite write)
+                return;
+            }
+            Log.d(TAG, "updating our caches for " + mPath);
+            fullyPopulateCaches();
+            mIsDirty.set(false);
+        }
+    }
+
     @Override
     public boolean onCreate() {
         mOpenHelper = new DatabaseHelper(getContext());
@@ -210,9 +260,65 @@
             return false;
         }
 
+        // Watch for external modifications to the database file,
+        // keeping our cache in sync.
+        // It's kinda lame to call mOpenHelper.getReadableDatabase()
+        // during onCreate(), but since ensureAndroidIdIsSet has
+        // already done it above and initialized/upgraded the
+        // database, might as well just use it...
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        sObserverInstance = new SettingsFileObserver(db.getPath());
+        sObserverInstance.startWatching();
+        startAsyncCachePopulation();
         return true;
     }
 
+    private void startAsyncCachePopulation() {
+        new Thread("populate-settings-caches") {
+            public void run() {
+                fullyPopulateCaches();
+            }
+        }.start();
+    }
+
+    private void fullyPopulateCaches() {
+        fullyPopulateCache("secure", sSecureCache);
+        fullyPopulateCache("system", sSystemCache);
+    }
+
+    // Slurp all values (if sane in number & size) into cache.
+    private void fullyPopulateCache(String table, SettingsCache cache) {
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        Cursor c = db.query(
+            table,
+            new String[] { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE },
+            null, null, null, null, null,
+            "" + (MAX_CACHE_ENTRIES + 1) /* limit */);
+        try {
+            synchronized (cache) {
+                cache.clear();
+                cache.setFullyMatchesDisk(true);  // optimistic
+                int rows = 0;
+                while (c.moveToNext()) {
+                    rows++;
+                    String name = c.getString(0);
+                    String value = c.getString(1);
+                    cache.populate(name, value);
+                }
+                if (rows > MAX_CACHE_ENTRIES) {
+                    // Somewhat redundant, as removeEldestEntry() will
+                    // have already done this, but to be explicit:
+                    cache.setFullyMatchesDisk(false);
+                    Log.d(TAG, "row count exceeds max cache entries for table " + table);
+                }
+                Log.d(TAG, "cache for settings table '" + table + "' fullycached=" +
+                      cache.fullyMatchesDisk());
+            }
+        } finally {
+            c.close();
+        }
+    }
+
     private boolean ensureAndroidIdIsSet() {
         final Cursor c = query(Settings.Secure.CONTENT_URI,
                 new String[] { Settings.NameValueTable.VALUE },
@@ -222,16 +328,11 @@
             final String value = c.moveToNext() ? c.getString(0) : null;
             if (value == null) {
                 final SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
-                String serial = SystemProperties.get("ro.serialno");
-                if (serial != null) {
-                    try {
-                        random.setSeed(serial.getBytes("UTF-8"));
-                    } catch (UnsupportedEncodingException ignore) {
-                        // stick with default seed
-                    }
-                }
+                String serial = SystemProperties.get("ro.serialno", "");
+                random.setSeed(
+                    (serial + System.nanoTime() + new SecureRandom().nextLong()).getBytes());
                 final String newAndroidIdValue = Long.toHexString(random.nextLong());
-                Log.d(TAG, "Generated and saved new ANDROID_ID");
+                Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]");
                 final ContentValues values = new ContentValues();
                 values.put(Settings.NameValueTable.NAME, Settings.Secure.ANDROID_ID);
                 values.put(Settings.NameValueTable.VALUE, newAndroidIdValue);
@@ -267,7 +368,19 @@
     private Bundle lookupValue(String table, SettingsCache cache, String key) {
         synchronized (cache) {
             if (cache.containsKey(key)) {
-                return cache.get(key);
+                Bundle value = cache.get(key);
+                if (value != TOO_LARGE_TO_CACHE_MARKER) {
+                    return value;
+                }
+                // else we fall through and read the value from disk
+            } else if (cache.fullyMatchesDisk()) {
+                // Fast path (very common).  Don't even try touch disk
+                // if we know we've slurped it all in.  Trying to
+                // touch the disk would mean waiting for yaffs2 to
+                // give us access, which could takes hundreds of
+                // milliseconds.  And we're very likely being called
+                // from somebody's UI thread...
+                return NULL_SETTING;
             }
         }
 
@@ -343,6 +456,7 @@
         checkWritePermissions(args);
         SettingsCache cache = SettingsCache.forTable(args.table);
 
+        sKnownMutationsInFlight.incrementAndGet();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         db.beginTransaction();
         try {
@@ -355,6 +469,7 @@
             db.setTransactionSuccessful();
         } finally {
             db.endTransaction();
+            sKnownMutationsInFlight.decrementAndGet();
         }
 
         sendNotify(uri);
@@ -454,8 +569,10 @@
             return Uri.withAppendedPath(url, name);
         }
 
+        sKnownMutationsInFlight.incrementAndGet();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         final long rowId = db.insert(args.table, null, initialValues);
+        sKnownMutationsInFlight.decrementAndGet();
         if (rowId <= 0) return null;
 
         SettingsCache.populate(cache, initialValues);  // before we notify
@@ -476,12 +593,15 @@
         }
         checkWritePermissions(args);
 
+        sKnownMutationsInFlight.incrementAndGet();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         int count = db.delete(args.table, args.where, args.args);
+        sKnownMutationsInFlight.decrementAndGet();
         if (count > 0) {
             SettingsCache.wipe(args.table);  // before we notify
             sendNotify(url);
         }
+        startAsyncCachePopulation();
         if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) deleted");
         return count;
     }
@@ -494,12 +614,15 @@
         }
         checkWritePermissions(args);
 
+        sKnownMutationsInFlight.incrementAndGet();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        sKnownMutationsInFlight.decrementAndGet();
         int count = db.update(args.table, initialValues, args.where, args.args);
         if (count > 0) {
             SettingsCache.wipe(args.table);  // before we notify
             sendNotify(url);
         }
+        startAsyncCachePopulation();
         if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) <- " + initialValues);
         return count;
     }
@@ -511,12 +634,12 @@
          * When a client attempts to openFile the default ringtone or
          * notification setting Uri, we will proxy the call to the current
          * default ringtone's Uri (if it is in the DRM or media provider).
-         */ 
+         */
         int ringtoneType = RingtoneManager.getDefaultType(uri);
         // Above call returns -1 if the Uri doesn't match a default type
         if (ringtoneType != -1) {
             Context context = getContext();
-            
+
             // Get the current value for the default sound
             Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
 
@@ -536,7 +659,7 @@
                             throw new FileNotFoundException(e.getMessage());
                         }
                     }
-                    
+
                     return context.getContentResolver().openFileDescriptor(soundUri, mode);
                 }
             }
@@ -612,13 +735,38 @@
      */
     private static final class SettingsCache extends LinkedHashMap<String, Bundle> {
 
-        public SettingsCache() {
+        private final String mCacheName;
+        private boolean mCacheFullyMatchesDisk = false;  // has the whole database slurped.
+
+        public SettingsCache(String name) {
             super(MAX_CACHE_ENTRIES, 0.75f /* load factor */, true /* access ordered */);
+            mCacheName = name;
+        }
+
+        /**
+         * Is the whole database table slurped into this cache?
+         */
+        public boolean fullyMatchesDisk() {
+            synchronized (this) {
+                return mCacheFullyMatchesDisk;
+            }
+        }
+
+        public void setFullyMatchesDisk(boolean value) {
+            synchronized (this) {
+                mCacheFullyMatchesDisk = value;
+            }
         }
 
         @Override
         protected boolean removeEldestEntry(Map.Entry eldest) {
-            return size() > MAX_CACHE_ENTRIES;
+            if (size() <= MAX_CACHE_ENTRIES) {
+                return false;
+            }
+            synchronized (this) {
+                mCacheFullyMatchesDisk = false;
+            }
+            return true;
         }
 
         /**
@@ -663,11 +811,15 @@
                 return;
             }
             String value = contentValues.getAsString(Settings.NameValueTable.VALUE);
-            synchronized (cache) {
+            cache.populate(name, value);
+        }
+
+        public void populate(String name, String value) {
+            synchronized (this) {
                 if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {
-                    cache.put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
+                    put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
                 } else {
-                    cache.remove(name);
+                    put(name, TOO_LARGE_TO_CACHE_MARKER);
                 }
             }
         }
@@ -683,6 +835,7 @@
             }
             synchronized (cache) {
                 cache.clear();
+                cache.mCacheFullyMatchesDisk = true;
             }
         }
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 7bc2e7d..18e2f479 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -5,6 +5,7 @@
         >
 
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <uses-permission android:name="android.permission.GET_TASKS" />
 
     <application
         android:persistent="true"
@@ -26,5 +27,11 @@
                 android:excludeFromRecents="true">
         </activity>
 
+        <activity android:name=".statusbar.RecentApplicationsActivity"
+            android:theme="@android:style/Theme.NoTitleBar"
+            android:excludeFromRecents="true"
+            android:exported="true">
+        </activity>
+
     </application>
 </manifest>
diff --git a/packages/SystemUI/res/drawable-hdpi/notification_dragger.png b/packages/SystemUI/res/drawable-hdpi/notification_dragger.png
deleted file mode 100644
index 71b5507..0000000
--- a/packages/SystemUI/res/drawable-hdpi/notification_dragger.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_0.png b/packages/SystemUI/res/drawable-mdpi/battery_0.png
index b5d36cc..e81638d 100644
--- a/packages/SystemUI/res/drawable-mdpi/battery_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/battery_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_10.png b/packages/SystemUI/res/drawable-mdpi/battery_10.png
new file mode 100644
index 0000000..a739f50
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_10.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_100.png b/packages/SystemUI/res/drawable-mdpi/battery_100.png
index 75cc409..9a66db38 100644
--- a/packages/SystemUI/res/drawable-mdpi/battery_100.png
+++ b/packages/SystemUI/res/drawable-mdpi/battery_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_20.png b/packages/SystemUI/res/drawable-mdpi/battery_20.png
deleted file mode 100644
index c0d0030..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_20.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_25.png b/packages/SystemUI/res/drawable-mdpi/battery_25.png
new file mode 100644
index 0000000..7ae7ebb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_25.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_40.png b/packages/SystemUI/res/drawable-mdpi/battery_40.png
deleted file mode 100644
index e301c08..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_40.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_5.png b/packages/SystemUI/res/drawable-mdpi/battery_5.png
new file mode 100644
index 0000000..1a15a21
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_5.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_50.png b/packages/SystemUI/res/drawable-mdpi/battery_50.png
new file mode 100644
index 0000000..7088066
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_60.png b/packages/SystemUI/res/drawable-mdpi/battery_60.png
deleted file mode 100644
index 0fde1fa..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_60.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_75.png b/packages/SystemUI/res/drawable-mdpi/battery_75.png
new file mode 100644
index 0000000..d4cfb6f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_75.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_80.png b/packages/SystemUI/res/drawable-mdpi/battery_80.png
deleted file mode 100644
index 15c4e1c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_80.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/notification_dragger.png b/packages/SystemUI/res/drawable-mdpi/notification_dragger.png
deleted file mode 100644
index fad1f32..0000000
--- a/packages/SystemUI/res/drawable-mdpi/notification_dragger.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_0.png b/packages/SystemUI/res/drawable-mdpi/signal_0.png
index 6533677..ff2727b 100644
--- a/packages/SystemUI/res/drawable-mdpi/signal_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_100.png b/packages/SystemUI/res/drawable-mdpi/signal_100.png
index e8976a2..55c34ff 100644
--- a/packages/SystemUI/res/drawable-mdpi/signal_100.png
+++ b/packages/SystemUI/res/drawable-mdpi/signal_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_20.png b/packages/SystemUI/res/drawable-mdpi/signal_20.png
deleted file mode 100644
index 651e2a9..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_20.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_25.png b/packages/SystemUI/res/drawable-mdpi/signal_25.png
new file mode 100644
index 0000000..a415596
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_25.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_40.png b/packages/SystemUI/res/drawable-mdpi/signal_40.png
deleted file mode 100644
index 6ba7906..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_40.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_50.png b/packages/SystemUI/res/drawable-mdpi/signal_50.png
new file mode 100644
index 0000000..3c5a708b0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_60.png b/packages/SystemUI/res/drawable-mdpi/signal_60.png
deleted file mode 100644
index 6d2e812..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_60.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_75.png b/packages/SystemUI/res/drawable-mdpi/signal_75.png
new file mode 100644
index 0000000..e176f37
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_75.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_80.png b/packages/SystemUI/res/drawable-mdpi/signal_80.png
deleted file mode 100644
index a152623..0000000
--- a/packages/SystemUI/res/drawable-mdpi/signal_80.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan1.png b/packages/SystemUI/res/drawable-mdpi/signal_scan1.png
new file mode 100644
index 0000000..f64b9cc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_scan1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan2.png b/packages/SystemUI/res/drawable-mdpi/signal_scan2.png
new file mode 100644
index 0000000..982feb4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_scan2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan3.png b/packages/SystemUI/res/drawable-mdpi/signal_scan3.png
new file mode 100644
index 0000000..a4c0689
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_scan3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan4.png b/packages/SystemUI/res/drawable-mdpi/signal_scan4.png
new file mode 100644
index 0000000..d1e95fe
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_scan4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png
index dd64746..5c57802 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
index 66a3677..a2527b3 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
index 4d19717..ce64926 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
index 830cbd5..c63c426 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png
index b129210..f219ded 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
index dcb2447..9e64fe8 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png
index 07d00cb..502acce 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_recent_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_default.png
new file mode 100644
index 0000000..4dd8dc7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png
new file mode 100644
index 0000000..350a3e9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_0.png b/packages/SystemUI/res/drawable-mdpi/wifi_0.png
new file mode 100644
index 0000000..e81638d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_100.png b/packages/SystemUI/res/drawable-mdpi/wifi_100.png
new file mode 100644
index 0000000..4468389
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_25.png b/packages/SystemUI/res/drawable-mdpi/wifi_25.png
new file mode 100644
index 0000000..0e389a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_25.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_50.png b/packages/SystemUI/res/drawable-mdpi/wifi_50.png
new file mode 100644
index 0000000..1cad0d1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_75.png b/packages/SystemUI/res/drawable-mdpi/wifi_75.png
new file mode 100644
index 0000000..dd2d7dc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_75.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan1.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan1.png
new file mode 100644
index 0000000..0e389a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_scan1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan2.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan2.png
new file mode 100644
index 0000000..34e7c70
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_scan2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan3.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan3.png
new file mode 100644
index 0000000..4c2a9e1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_scan3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan4.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan4.png
new file mode 100644
index 0000000..b504131
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_scan4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/battery.xml b/packages/SystemUI/res/drawable/battery.xml
index c2294d1..3bc0c74 100644
--- a/packages/SystemUI/res/drawable/battery.xml
+++ b/packages/SystemUI/res/drawable/battery.xml
@@ -20,16 +20,11 @@
 
 <level-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:maxLevel="1" android:drawable="@drawable/battery_0" />
-    <item android:maxLevel="5">
-        <animation-list android:oneshot="false">
-            <item android:drawable="@drawable/battery_0" android:duration="250" />
-            <item android:drawable="@drawable/battery_20" android:duration="250" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="20" android:drawable="@drawable/battery_20" />
-    <item android:maxLevel="40" android:drawable="@drawable/battery_40" />
-    <item android:maxLevel="60" android:drawable="@drawable/battery_60" />
-    <item android:maxLevel="80" android:drawable="@drawable/battery_80" />
+    <item android:maxLevel="5" android:drawable="@drawable/battery_5" />
+    <item android:maxLevel="10" android:drawable="@drawable/battery_10" />
+    <item android:maxLevel="25" android:drawable="@drawable/battery_25" />
+    <item android:maxLevel="50" android:drawable="@drawable/battery_50" />
+    <item android:maxLevel="75" android:drawable="@drawable/battery_75" />
     <item android:maxLevel="101" android:drawable="@drawable/battery_100" />
 </level-list>
 
diff --git a/packages/SystemUI/res/drawable/battery_charging.xml b/packages/SystemUI/res/drawable/battery_charging.xml
index 2fd0c6d..c9b77dd 100644
--- a/packages/SystemUI/res/drawable/battery_charging.xml
+++ b/packages/SystemUI/res/drawable/battery_charging.xml
@@ -19,33 +19,27 @@
 -->
 
 <level-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:maxLevel="20">
+    <item android:maxLevel="25">
         <animation-list android:oneshot="false">
             <item android:drawable="@drawable/battery_0" android:duration="1000" />
-            <item android:drawable="@drawable/battery_20" android:duration="1000" />
+            <item android:drawable="@drawable/battery_25" android:duration="1000" />
         </animation-list>
     </item>
-    <item android:maxLevel="40">
+    <item android:maxLevel="50">
         <animation-list android:oneshot="false">
-            <item android:drawable="@drawable/battery_20" android:duration="1000" />
-            <item android:drawable="@drawable/battery_40" android:duration="1000" />
+            <item android:drawable="@drawable/battery_25" android:duration="1000" />
+            <item android:drawable="@drawable/battery_50" android:duration="1000" />
         </animation-list>
     </item>
-    <item android:maxLevel="60">
+    <item android:maxLevel="75">
         <animation-list android:oneshot="false">
-            <item android:drawable="@drawable/battery_40" android:duration="1000" />
-            <item android:drawable="@drawable/battery_60" android:duration="1000" />
-        </animation-list>
-    </item>
-    <item android:maxLevel="80">
-        <animation-list android:oneshot="false">
-            <item android:drawable="@drawable/battery_60" android:duration="1000" />
-            <item android:drawable="@drawable/battery_80" android:duration="1000" />
+            <item android:drawable="@drawable/battery_50" android:duration="1000" />
+            <item android:drawable="@drawable/battery_75" android:duration="1000" />
         </animation-list>
     </item>
     <item android:maxLevel="92">
         <animation-list android:oneshot="false">
-            <item android:drawable="@drawable/battery_80" android:duration="1000" />
+            <item android:drawable="@drawable/battery_75" android:duration="1000" />
             <item android:drawable="@drawable/battery_100" android:duration="1000" />
         </animation-list>
     </item>
diff --git a/packages/SystemUI/res/drawable/signal.xml b/packages/SystemUI/res/drawable/signal.xml
index 8b4f56b..7367418 100644
--- a/packages/SystemUI/res/drawable/signal.xml
+++ b/packages/SystemUI/res/drawable/signal.xml
@@ -20,11 +20,9 @@
 
 <level-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:maxLevel="01" android:drawable="@drawable/signal_0" />
-    <item android:maxLevel="20" android:drawable="@drawable/signal_20" />
-    <item android:maxLevel="40" android:drawable="@drawable/signal_40" />
-    <item android:maxLevel="60" android:drawable="@drawable/signal_60" />
-    <item android:maxLevel="80" android:drawable="@drawable/signal_80" />
+    <item android:maxLevel="25" android:drawable="@drawable/signal_25" />
+    <item android:maxLevel="50" android:drawable="@drawable/signal_50" />
+    <item android:maxLevel="75" android:drawable="@drawable/signal_75" />
     <item android:maxLevel="101" android:drawable="@drawable/signal_100" />
-
 </level-list>
 
diff --git a/packages/SystemUI/res/drawable/signal_scan.xml b/packages/SystemUI/res/drawable/signal_scan.xml
new file mode 100644
index 0000000..fe21992
--- /dev/null
+++ b/packages/SystemUI/res/drawable/signal_scan.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/signal_scan1" android:duration="500" />
+    <item android:drawable="@drawable/signal_scan2" android:duration="500" />
+    <item android:drawable="@drawable/signal_scan3" android:duration="500" />
+    <item android:drawable="@drawable/signal_scan4" android:duration="500" />
+    <item android:drawable="@drawable/signal_scan3" android:duration="500" />
+    <item android:drawable="@drawable/signal_scan2" android:duration="500" />
+</animation-list>
+
diff --git a/packages/SystemUI/res/drawable/wifi.xml b/packages/SystemUI/res/drawable/wifi.xml
new file mode 100644
index 0000000..6e63af6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/wifi.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:maxLevel="01" android:drawable="@drawable/wifi_0" />
+    <item android:maxLevel="25" android:drawable="@drawable/wifi_25" />
+    <item android:maxLevel="50" android:drawable="@drawable/wifi_50" />
+    <item android:maxLevel="75" android:drawable="@drawable/wifi_75" />
+    <item android:maxLevel="101" android:drawable="@drawable/wifi_100" />
+
+</level-list>
+
diff --git a/packages/SystemUI/res/drawable/wifi_scan.xml b/packages/SystemUI/res/drawable/wifi_scan.xml
new file mode 100644
index 0000000..5888d0e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/wifi_scan.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/wifi_scan1" android:duration="500" />
+    <item android:drawable="@drawable/wifi_scan2" android:duration="500" />
+    <item android:drawable="@drawable/wifi_scan3" android:duration="500" />
+    <item android:drawable="@drawable/wifi_scan4" android:duration="500" />
+    <item android:drawable="@drawable/wifi_scan3" android:duration="500" />
+    <item android:drawable="@drawable/wifi_scan2" android:duration="500" />
+</animation-list>
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index ffb1571..481bcde 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -101,8 +101,8 @@
         </LinearLayout>
 
         <include layout="@layout/status_bar_center"
-            android:layout_width="100dip"
-            android:layout_height="wrap_content"
+            android:layout_width="160dip"
+            android:layout_height="match_parent"
             android:layout_centerInParent="true"
             />
 
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
index 775fea0..5bf8316 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
@@ -18,8 +18,8 @@
 <RelativeLayout android:id="@+id/systemInfo"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
-    android:layout_width="100dip"
-    android:layout_height="wrap_content"
+    android:layout_width="160dip"
+    android:layout_height="match_parent"
     android:layout_centerInParent="true"
     android:clickable="true"
     android:onClick="systemInfoClicked"
@@ -27,8 +27,9 @@
     <com.android.systemui.statusbar.Clock
         style="@*android:style/TextAppearance.StatusBar.Icon"
         android:id="@+id/clock"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_width="64dip"
+        android:layout_height="48dip"
+        android:layout_centerInParent="true"
         android:singleLine="true"
         android:gravity="center"
         android:textSize="16sp"
@@ -37,18 +38,16 @@
         />
     <ImageView
         android:id="@+id/battery"
-        android:layout_width="50dip"
-        android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_below="@id/clock"
+        android:layout_width="48dip"
+        android:layout_height="48dip"
+        android:layout_toLeftOf="@id/clock"
         android:src="@drawable/battery"
         />
     <ImageView
         android:id="@+id/signal"
-        android:layout_width="50dip"
-        android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_below="@id/clock"
+        android:layout_width="48dip"
+        android:layout_height="48dip"
+        android:layout_toRightOf="@id/clock"
         android:src="@drawable/signal"
         />
 </RelativeLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
index 273674f..36c6388 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
@@ -92,8 +92,10 @@
             android:layout_alignParentLeft="true"
             >
             <ImageView android:id="@+id/battery_meter"
-                android:layout_width="wrap_content"
+                android:layout_width="96dip"
                 android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:scaleType="centerCrop"
                 android:src="@drawable/battery"
                 />
 
@@ -129,8 +131,10 @@
             android:layout_alignParentRight="true"
             >
             <ImageView android:id="@+id/signal_meter"
-                android:layout_width="wrap_content"
+                android:layout_width="96dip"
                 android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:scaleType="centerCrop"
                 android:src="@drawable/signal"
                 />
 
diff --git a/packages/SystemUI/res/values-xlarge/strings.xml b/packages/SystemUI/res/values-xlarge/strings.xml
index 4aa4b47..3c59c92 100644
--- a/packages/SystemUI/res/values-xlarge/strings.xml
+++ b/packages/SystemUI/res/values-xlarge/strings.xml
@@ -20,4 +20,58 @@
     <!-- The text for the button in the notification window-shade that clears
          all of the currently visible notifications. -->
     <string name="status_bar_clear_all_button">Clear all</string>
+
+    <!-- System panel ("Quick Settings") -->
+
+    <!-- Text to display underneath the graphical battery meter. Should
+         include the word for "battery" and a place for the percentage charge
+         available. [CHAR LIMIT=20] -->
+    <string name="system_panel_battery_meter_format">
+        Battery: <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g>
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         no connection is available. [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_disconnected">
+        no internet connection
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying information about a connected, named Wi-Fi network.
+         Should include the word for "Wi-Fi" and a placeholder for the
+         wireless network's SSID. [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_wifi_ssid_format">
+        Wi-Fi: <xliff:g id="ssid">%s</xliff:g>
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying Wi-Fi status and Wi-Fi is connected to a network
+         whose SSID is not available.
+         [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_wifi_nossid">
+        Wi-Fi: connected
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying Wi-Fi status and Wi-Fi is in the process of
+         connecting to a network.  [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_wifi_connecting">
+        Wi-Fi: connecting…
+    </string>
+ 
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying mobile data (3G) status and a network connection is
+         available.
+         [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_data_connected">
+        Mobile data: connected
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying mobile data (3G) status and a network connection is
+         unavailable.
+         [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_data_connecting">
+        Mobile data: connecting…
+    </string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
index 91b583b..33acecb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -1485,7 +1485,7 @@
             String action = intent.getAction();
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
-                //collapse();
+                animateCollapse();
             }
             else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
                 updateResources();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/statusbar/RecentApplicationsActivity.java
new file mode 100644
index 0000000..a5ba7e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RecentApplicationsActivity.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.systemui.statusbar;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.android.internal.widget.CarouselView;
+import com.android.internal.widget.CarouselRS.CarouselCallback;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.IThumbnailReceiver;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.Bitmap.Config;
+import android.graphics.drawable.Drawable;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+
+public class RecentApplicationsActivity extends Activity {
+    private static final String TAG = "RecentApplicationsActivity";
+    private static boolean DBG = true;
+    private static final int CARD_SLOTS = 56;
+    private static final int VISIBLE_SLOTS = 7;
+    private static final int MAX_TASKS = VISIBLE_SLOTS * 2;
+    private ActivityManager mActivityManager;
+    private List<RunningTaskInfo> mRunningTaskList;
+    private boolean mPortraitMode = true;
+    private ArrayList<ActivityDescription> mActivityDescriptions
+            = new ArrayList<ActivityDescription>();
+    private CarouselView mCarouselView;
+    private View mNoRecentsView;
+    private Bitmap mBlankBitmap = Bitmap.createBitmap(
+            new int[] {0xff808080, 0xffffffff, 0xff808080, 0xffffffff}, 2, 2, Config.RGB_565);
+
+    static class ActivityDescription {
+        int id;
+        Bitmap thumbnail; // generated by Activity.onCreateThumbnail()
+        Drawable icon; // application package icon
+        String label; // application package label
+        String description; // generated by Activity.onCreateDescription()
+        Intent intent; // launch intent for application
+        Matrix matrix; // arbitrary rotation matrix to correct orientation
+        int position; // position in list
+
+        public ActivityDescription(Bitmap _thumbnail,
+                Drawable _icon, String _label, String _desc, int _id, int _pos)
+        {
+            thumbnail = _thumbnail;
+            icon = _icon;
+            label = _label;
+            description = _desc;
+            id = _id;
+            position = _pos;
+        }
+
+        public void clear() {
+            icon = null;
+            thumbnail = null;
+            label = null;
+            description = null;
+            intent = null;
+            matrix = null;
+            id = -1;
+            position = -1;
+        }
+    };
+
+    private ActivityDescription findActivityDescription(int id) {
+        for (int i = 0; i < mActivityDescriptions.size(); i++) {
+            ActivityDescription item = mActivityDescriptions.get(i);
+            if (item != null && item.id == id) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    final CarouselCallback mCarouselCallback = new CarouselCallback() {
+
+        public void onAnimationFinished() {
+
+        }
+
+        public void onAnimationStarted() {
+
+        }
+
+        public void onCardSelected(int n) {
+            if (n < mActivityDescriptions.size()) {
+                ActivityDescription item = mActivityDescriptions.get(n);
+                // prepare a launch intent and send it
+                if (item.intent != null) {
+                    item.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+                    try {
+                        if (DBG) Log.v(TAG, "Starting intent " + item.intent);
+                        startActivity(item.intent);
+                        //overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);
+                    } catch (ActivityNotFoundException e) {
+                        if (DBG) Log.w("Recent", "Unable to launch recent task", e);
+                    }
+                    finish();
+                }
+            }
+        }
+
+        public void onInvalidateTexture(int n) {
+
+        }
+
+        public void onRequestGeometry(int n) {
+
+        }
+
+        public void onInvalidateGeometry(int n) {
+
+        }
+
+        public void onRequestTexture(final int n) {
+            if (DBG) Log.v(TAG, "onRequestTexture(" + n + ")");
+            if (n < mActivityDescriptions.size()) {
+                mCarouselView.post(new Runnable() {
+                    public void run() {
+                        ActivityDescription info = mActivityDescriptions.get(n);
+                        if (info != null) {
+                            if (DBG) Log.v(TAG, "FOUND ACTIVITY THUMBNAIL " + info.thumbnail);
+                            Bitmap bitmap = info.thumbnail == null ? mBlankBitmap : info.thumbnail;
+                            mCarouselView.setTextureForItem(n, bitmap);
+                        } else {
+                            if (DBG) Log.v(TAG, "FAILED TO GET ACTIVITY THUMBNAIL FOR ITEM " + n);
+                        }
+                    }
+                });
+            }
+        }
+    };
+
+    private final IThumbnailReceiver mThumbnailReceiver = new IThumbnailReceiver.Stub() {
+
+        public void finished() throws RemoteException {
+
+        }
+
+        public void newThumbnail(final int id, final Bitmap bitmap, CharSequence description)
+                throws RemoteException {
+            int w = bitmap.getWidth();
+            int h = bitmap.getHeight();
+            if (DBG) Log.v(TAG, "New thumbnail for id=" + id + ", dimensions=" + w + "x" + h
+                    + " description '" + description + "'");
+            ActivityDescription info = findActivityDescription(id);
+            if (info != null) {
+                info.thumbnail = bitmap;
+                final int thumbWidth = bitmap.getWidth();
+                final int thumbHeight = bitmap.getHeight();
+                if ((mPortraitMode && thumbWidth > thumbHeight)
+                        || (!mPortraitMode && thumbWidth < thumbHeight)) {
+                    Matrix matrix = new Matrix();
+                    matrix.setRotate(90.0f, (float) thumbWidth / 2, (float) thumbHeight / 2);
+                    info.matrix = matrix;
+                } else {
+                    info.matrix = null;
+                }
+                mCarouselView.setTextureForItem(info.position, info.thumbnail);
+            } else {
+                if (DBG) Log.v(TAG, "Can't find view for id " + id);
+            }
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final Resources res = getResources();
+        final View decorView = getWindow().getDecorView();
+
+        getWindow().getDecorView().setBackgroundColor(0x80000000);
+        setContentView(R.layout.recent_apps_activity);
+        mCarouselView = (CarouselView)findViewById(R.id.carousel);
+        mNoRecentsView = (View) findViewById(R.id.no_applications_message);
+        //mCarouselView = new CarouselView(this);
+        //setContentView(mCarouselView);
+        mCarouselView.setSlotCount(CARD_SLOTS);
+        mCarouselView.setVisibleSlots(VISIBLE_SLOTS);
+        mCarouselView.createCards(1);
+        mCarouselView.setStartAngle((float) -(2.0f*Math.PI * 5 / CARD_SLOTS));
+        mCarouselView.setDefaultBitmap(mBlankBitmap);
+        mCarouselView.setLoadingBitmap(mBlankBitmap);
+        mCarouselView.setCallback(mCarouselCallback);
+        mCarouselView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+
+        mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+        mPortraitMode = decorView.getHeight() > decorView.getWidth();
+
+        refresh();
+
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        refresh();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        mPortraitMode = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT;
+        if (DBG) Log.v(TAG, "CONFIG CHANGE, mPortraitMode = " + mPortraitMode);
+        refresh();
+    }
+
+    void updateRunningTasks() {
+        mRunningTaskList = mActivityManager.getRunningTasks(MAX_TASKS, 0, mThumbnailReceiver);
+        if (DBG) Log.v(TAG, "Portrait: " + mPortraitMode);
+        for (RunningTaskInfo r : mRunningTaskList) {
+            if (r.thumbnail != null) {
+                int thumbWidth = r.thumbnail.getWidth();
+                int thumbHeight = r.thumbnail.getHeight();
+                if (DBG) Log.v(TAG, "Got thumbnail " + thumbWidth + "x" + thumbHeight);
+                ActivityDescription desc = findActivityDescription(r.id);
+                if (desc != null) {
+                    desc.thumbnail = r.thumbnail;
+                    desc.label = r.topActivity.flattenToShortString();
+                    if ((mPortraitMode && thumbWidth > thumbHeight)
+                            || (!mPortraitMode && thumbWidth < thumbHeight)) {
+                        Matrix matrix = new Matrix();
+                        matrix.setRotate(90.0f, (float) thumbWidth / 2, (float) thumbHeight / 2);
+                        desc.matrix = matrix;
+                    }
+                } else {
+                    if (DBG) Log.v(TAG, "Couldn't find ActivityDesc for id=" + r.id);
+                }
+            } else {
+                if (DBG) Log.v(TAG, "*** RUNNING THUMBNAIL WAS NULL ***");
+            }
+        }
+        mCarouselView.createCards(mActivityDescriptions.size());
+    }
+
+    private void updateRecentTasks() {
+        final PackageManager pm = getPackageManager();
+        final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+
+        final List<ActivityManager.RecentTaskInfo> recentTasks =
+                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+
+        ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
+                    .resolveActivityInfo(pm, 0);
+
+        // IconUtilities iconUtilities = new IconUtilities(this); // FIXME
+
+        int numTasks = recentTasks.size();
+        mActivityDescriptions.clear();
+        for (int i = 0, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
+            final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
+
+            Intent intent = new Intent(recentInfo.baseIntent);
+            if (recentInfo.origActivity != null) {
+                intent.setComponent(recentInfo.origActivity);
+            }
+
+            // Skip the current home activity.
+            if (homeInfo != null
+                    && homeInfo.packageName.equals(intent.getComponent().getPackageName())
+                    && homeInfo.name.equals(intent.getComponent().getClassName())) {
+                continue;
+            }
+
+            intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+                    | Intent.FLAG_ACTIVITY_NEW_TASK);
+            final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+            if (resolveInfo != null) {
+                final ActivityInfo info = resolveInfo.activityInfo;
+                final String title = info.loadLabel(pm).toString();
+                Drawable icon = info.loadIcon(pm);
+
+                int id = recentTasks.get(i).id;
+                if (id != -1 && title != null && title.length() > 0 && icon != null) {
+                    // icon = null; FIXME: iconUtilities.createIconDrawable(icon);
+                    ActivityDescription item = new ActivityDescription(
+                            null, icon, title, null, id, index);
+                    item.intent = intent;
+                    mActivityDescriptions.add(item);
+                    if (DBG) Log.v(TAG, "Added item[" + index
+                            + "], id=" + item.id
+                            + ", title=" + item.label);
+                    ++index;
+                } else {
+                    if (DBG) Log.v(TAG, "SKIPPING item " + id);
+                }
+            }
+        }
+    }
+
+    private void refresh() {
+        updateRecentTasks();
+        updateRunningTasks();
+        if (mActivityDescriptions.size() == 0) {
+            // show "No Recent Takss"
+            mNoRecentsView.setVisibility(View.VISIBLE);
+            mCarouselView.setVisibility(View.GONE);
+        } else {
+            mNoRecentsView.setVisibility(View.GONE);
+            mCarouselView.setVisibility(View.VISIBLE);
+            mCarouselView.createCards(mActivityDescriptions.size());
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
index a03393b..dfa2d10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
@@ -45,6 +45,7 @@
 import android.provider.Settings;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Slog;
@@ -66,11 +67,18 @@
 
 import java.util.List;
 
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.cdma.EriInfo;
+import com.android.internal.telephony.cdma.TtyIntent;
+
 import com.android.systemui.statusbar.*;
 import com.android.systemui.R;
 
 public class SystemPanel extends LinearLayout {
     private static final String TAG = "SystemPanel";
+    private static final boolean DEBUG = TabletStatusBarService.DEBUG;
+    private static final boolean DEBUG_SIGNAL = true;
 
     private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 5;
     private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
@@ -93,7 +101,22 @@
 
     private final AudioManager mAudioManager;
     private final WifiManager mWifiManager;
+    private final TelephonyManager mPhone;
 
+    // state trackers for telephony code
+    IccCard.State mSimState = IccCard.State.READY;
+    int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
+    int mDataState = TelephonyManager.DATA_DISCONNECTED;
+    ServiceState mServiceState;
+    SignalStrength mSignalStrength;
+
+    // state for the meters
+    boolean mWifiEnabled, mWifiConnected;
+    int mWifiLevel;
+    String mWifiSsid;
+
+    boolean mDataEnabled, mDataConnected, mDataRoaming;
+    int mDataLevel;
 
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -107,18 +130,154 @@
                     || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)
                     || action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)
                     || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                updateWifi(intent);
+                updateWifiState(intent);
+            } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+                updateSimState(intent);
             }
         }
     };
 
-    boolean mWifiEnabled, mWifiConnected;
-    int mWifiLevel;
-    String mWifiSsid;
+    private final void updateSimState(Intent intent) {
+        String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
+        if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+            mSimState = IccCard.State.ABSENT;
+        }
+        else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+            mSimState = IccCard.State.READY;
+        }
+        else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+            final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
+            if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+                mSimState = IccCard.State.PIN_REQUIRED;
+            }
+            else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+                mSimState = IccCard.State.PUK_REQUIRED;
+            }
+            else {
+                mSimState = IccCard.State.NETWORK_LOCKED;
+            }
+        } else {
+            mSimState = IccCard.State.UNKNOWN;
+        }
+        updateDataState();
+    }
 
-    private void updateWifi(Intent intent) {
-        if (TabletStatusBarService.DEBUG)
-            Slog.d(TabletStatusBarService.TAG, "updateWifi: " + intent);
+    private boolean isCdma() {
+        return (mSignalStrength != null) && !mSignalStrength.isGsm();
+    }
+
+    private boolean isEvdo() {
+        return ( (mServiceState != null)
+                 && ((mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
+                     || (mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
+                     || (mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_B)));
+    }
+
+    private boolean hasService() {
+        if (mServiceState != null) {
+            switch (mServiceState.getState()) {
+                case ServiceState.STATE_OUT_OF_SERVICE:
+                case ServiceState.STATE_POWER_OFF:
+                    return false;
+                default:
+                    return true;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    private int getCdmaLevel() {
+        if (mSignalStrength == null) return 0;
+        final int cdmaDbm = mSignalStrength.getCdmaDbm();
+        final int cdmaEcio = mSignalStrength.getCdmaEcio();
+        int levelDbm = 0;
+        int levelEcio = 0;
+
+        if (cdmaDbm >= -75) levelDbm = 4;
+        else if (cdmaDbm >= -85) levelDbm = 3;
+        else if (cdmaDbm >= -95) levelDbm = 2;
+        else if (cdmaDbm >= -100) levelDbm = 1;
+        else levelDbm = 0;
+
+        // Ec/Io are in dB*10
+        if (cdmaEcio >= -90) levelEcio = 4;
+        else if (cdmaEcio >= -110) levelEcio = 3;
+        else if (cdmaEcio >= -130) levelEcio = 2;
+        else if (cdmaEcio >= -150) levelEcio = 1;
+        else levelEcio = 0;
+
+        return (levelDbm < levelEcio) ? levelDbm : levelEcio;
+    }
+
+    private int getEvdoLevel() {
+        if (mSignalStrength == null) return 0;
+        int evdoDbm = mSignalStrength.getEvdoDbm();
+        int evdoSnr = mSignalStrength.getEvdoSnr();
+        int levelEvdoDbm = 0;
+        int levelEvdoSnr = 0;
+
+        if (evdoDbm >= -65) levelEvdoDbm = 4;
+        else if (evdoDbm >= -75) levelEvdoDbm = 3;
+        else if (evdoDbm >= -90) levelEvdoDbm = 2;
+        else if (evdoDbm >= -105) levelEvdoDbm = 1;
+        else levelEvdoDbm = 0;
+
+        if (evdoSnr >= 7) levelEvdoSnr = 4;
+        else if (evdoSnr >= 5) levelEvdoSnr = 3;
+        else if (evdoSnr >= 3) levelEvdoSnr = 2;
+        else if (evdoSnr >= 1) levelEvdoSnr = 1;
+        else levelEvdoSnr = 0;
+
+        return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+    }
+
+    private void updateDataState() {
+        mDataConnected = hasService() && (mDataState == TelephonyManager.DATA_CONNECTED);
+
+        if (isCdma()) {
+            // these functions return a value from 0 to 4, inclusive
+            if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
+                mDataLevel = getEvdoLevel() * 25;
+            } else {
+                mDataLevel = getCdmaLevel() * 25;
+            }
+        } else {
+            // GSM
+            
+            int asu = (mSignalStrength == null) ? 0 : mSignalStrength.getGsmSignalStrength();
+
+            // asu on [0,31]; 99 = unknown
+            // Android has historically shown anything >=12 as "full"
+            // XXX: tune this based on Industry Best Practices(TM)
+            if (asu <= 2 || asu == 99) mDataLevel = 0;
+            else mDataLevel = (int)(((float)Math.max(asu, 15) / 15) * 100);
+
+            mDataRoaming = mPhone.isNetworkRoaming();
+
+            mDataConnected = mDataConnected
+                && (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN);
+        }
+
+        if (DEBUG_SIGNAL || DEBUG) {
+            Slog.d(TAG, "updateDataState: connected=" + mDataConnected 
+                    + " level=" + mDataLevel
+                    + " isEvdo=" + isEvdo()
+                    + " isCdma=" + isCdma()
+                    + " mPhoneState=" + mPhoneState
+                    + " mDataState=" + mDataState
+                    );
+        }
+
+        refreshSignalMeters();
+    }
+
+    private void updateWifiState(Intent intent) {
+        if (DEBUG)
+            Slog.d(TAG, "updateWifiState: " + intent);
 
         final String action = intent.getAction();
         final boolean wasConnected = mWifiConnected;
@@ -142,8 +301,8 @@
 
         if (mWifiConnected && !wasConnected) {
             WifiInfo info = mWifiManager.getConnectionInfo();
-            if (TabletStatusBarService.DEBUG)
-                Slog.d(TabletStatusBarService.TAG, "updateWifi: just connected: info=" + info);
+            if (DEBUG)
+                Slog.d(TAG, "updateWifiState: just connected: info=" + info);
 
             if (info != null) {
                 // grab the initial signal strength
@@ -164,22 +323,40 @@
             }
         }
 
-        if (!mWifiEnabled) {
-            mWifiSsid = "disabled";
-            mWifiLevel = 0;
-        } else if (!mWifiConnected) {
-            mWifiSsid = "disconnected";
-            mWifiLevel = 0;
-        } else if (mWifiSsid == null) {
-            mWifiSsid = "unknown";
+        refreshSignalMeters();
+    }
+
+    // figure out what to show: first wifi, then 3G, then nothing
+    void refreshSignalMeters() {
+        if (mSignalMeter == null) return; // no UI yet
+
+        Context ctxt = getContext();
+
+        String text = null;
+        int level = 0;
+
+        if (mWifiConnected) {
+            if (mWifiSsid == null) {
+                text = ctxt.getString(R.string.system_panel_signal_meter_wifi_nossid);
+            } else {
+                text = ctxt.getString(R.string.system_panel_signal_meter_wifi_ssid_format,
+                                      mWifiSsid);
+            }
+            level = mWifiLevel;
+        } else if (mDataConnected) {
+            text = ctxt.getString(R.string.system_panel_signal_meter_data_connected);
+            level = mDataLevel;
+        } else {
+            text = ctxt.getString(R.string.system_panel_signal_meter_disconnected);
+            level = 0;
         }
 
-        mSignalMeter.setImageResource(R.drawable.signal);
-        mSignalMeter.setImageLevel(mWifiLevel);
-        mSignalText.setText(String.format("Wi-Fi: %s", mWifiSsid)); // XXX: localize
+        mSignalMeter.setImageResource(mWifiConnected ? R.drawable.wifi : R.drawable.signal);
+        mSignalMeter.setImageLevel(level);
+        mSignalText.setText(text);
 
         // hack for now
-        mBar.setWifiMeter(mWifiLevel);
+        mBar.setSignalMeter(level, mWifiConnected);
     }
 
     public void setBar(TabletStatusBarService bar) {
@@ -192,7 +369,8 @@
 
         mBatteryMeter.setImageResource(plugged ? R.drawable.battery_charging : R.drawable.battery);
         mBatteryMeter.setImageLevel(level);
-        mBatteryText.setText(String.format("Battery: %d%%", level));
+        mBatteryText.setText(getContext()
+                .getString(R.string.system_panel_battery_meter_format, level));
 
         // hack for now
         mBar.setBatteryMeter(level, plugged);
@@ -208,13 +386,21 @@
         // get notified of phone state changes
         TelephonyManager telephonyManager =
                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-        telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+        telephonyManager.listen(mPhoneStateListener,
+                          PhoneStateListener.LISTEN_SERVICE_STATE
+                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+                        | PhoneStateListener.LISTEN_CALL_STATE
+                        | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+                        | PhoneStateListener.LISTEN_DATA_ACTIVITY);
 
         // wifi status info
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
         
-        // audio status notifications
+        // audio status 
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+
+        // mobile data 
+        mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
     }
 
     public void onAttachedToWindow() {
@@ -259,6 +445,7 @@
             }
         });
 
+        // register for broadcasts
         IntentFilter filter = new IntentFilter();
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
@@ -266,8 +453,9 @@
         filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         getContext().registerReceiver(mReceiver, filter);
-
+        
         mBatteryMeter = (ImageView)findViewById(R.id.battery_meter);
         mBatteryMeter.setImageResource(R.drawable.battery);
         mBatteryMeter.setImageLevel(0);
@@ -277,6 +465,8 @@
 
         mBatteryText = (TextView)findViewById(R.id.battery_info);
         mSignalText = (TextView)findViewById(R.id.signal_info);
+
+        refreshSignalMeters();
     }
 
     public void onDetachedFromWindow() {
@@ -362,11 +552,42 @@
     PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
         @Override
         public void onServiceStateChanged(ServiceState serviceState) {
-            Slog.d(TAG, "phone service state changed: " + serviceState.getState());
+            if (DEBUG_SIGNAL || DEBUG) {
+                Slog.d(TAG, "phone service state changed: " + serviceState.getState());
+            }
+            mServiceState = serviceState;
             mAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
             if (mAirplaneButton != null) {
                 mAirplaneButton.setAlpha(mAirplaneMode ? 0xFF : 0x7F);
             }
+            updateDataState();
+        }
+        @Override
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            if (DEBUG_SIGNAL || DEBUG) {
+                Slog.d(TAG, "onSignalStrengthsChanged: " + signalStrength);
+            }
+            mSignalStrength = signalStrength;
+            updateDataState();
+        }
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            mPhoneState = state;
+            // In cdma, if a voice call is made, RSSI should switch to 1x.
+            if (isCdma()) {
+                updateDataState();
+            }
+        }
+
+        @Override
+        public void onDataConnectionStateChanged(int state, int networkType) {
+            if (DEBUG_SIGNAL || DEBUG) {
+                Slog.d(TAG, "onDataConnectionStateChanged: state=" + state 
+                        + " type=" + networkType);
+            }
+            mDataState = state;
+//            updateDataNetType(networkType);
+            updateDataState();
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index 18b9b41..087671a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -212,10 +212,15 @@
         mBatteryMeter.setImageLevel(level);
     }
 
-    public void setWifiMeter(int level) {
-        if (DEBUG) Slog.d(TAG, "wifi=" + level);
-        mSignalMeter.setImageResource(R.drawable.signal);
-        mSignalMeter.setImageLevel(level);
+    public void setSignalMeter(int level, boolean isWifi) {
+        if (DEBUG) Slog.d(TAG, "signal=" + level);
+        if (level < 0) {
+            mSignalMeter.setImageResource(isWifi ? R.drawable.wifi_scan : R.drawable.signal_scan);
+            mSignalMeter.setImageLevel(0);
+        } else {
+            mSignalMeter.setImageResource(isWifi ? R.drawable.wifi : R.drawable.signal);
+            mSignalMeter.setImageLevel(level);
+        }
     }
 
     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 2bb4456..ca9a484 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -199,6 +199,13 @@
             /* Custom title feature is enabled and the user is trying to enable another feature */
             throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
         }
+        if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) {
+            return false; // Ignore. No title dominates.
+        }
+        if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_NO_TITLE) {
+            // Remove the action bar feature if we have no title. No title dominates.
+            removeFeature(FEATURE_ACTION_BAR);
+        }
         return super.requestFeature(featureId);
     }
 
@@ -364,29 +371,32 @@
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
-        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
-        if ((st != null) && (st.menu != null)) {
-            final MenuBuilder menuBuilder = (MenuBuilder) st.menu;
+        // Action bars handle their own menu state
+        if (mActionBar == null) {
+            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+            if ((st != null) && (st.menu != null)) {
+                final MenuBuilder menuBuilder = (MenuBuilder) st.menu;
 
-            if (st.isOpen) {
-                // Freeze state
-                final Bundle state = new Bundle();
-                menuBuilder.saveHierarchyState(state);
+                if (st.isOpen) {
+                    // Freeze state
+                    final Bundle state = new Bundle();
+                    menuBuilder.saveHierarchyState(state);
 
-                // Remove the menu views since they need to be recreated
-                // according to the new configuration
-                clearMenuViews(st);
+                    // Remove the menu views since they need to be recreated
+                    // according to the new configuration
+                    clearMenuViews(st);
 
-                // Re-open the same menu
-                reopenMenu(false);
+                    // Re-open the same menu
+                    reopenMenu(false);
 
-                // Restore state
-                menuBuilder.restoreHierarchyState(state);
+                    // Restore state
+                    menuBuilder.restoreHierarchyState(state);
 
-            } else {
-                // Clear menu views so on next menu opening, it will use
-                // the proper layout
-                clearMenuViews(st);
+                } else {
+                    // Clear menu views so on next menu opening, it will use
+                    // the proper layout
+                    clearMenuViews(st);
+                }
             }
         }
     }
@@ -1515,6 +1525,7 @@
     static private final String FOCUSED_ID_TAG = "android:focusedViewId";
     static private final String VIEWS_TAG = "android:views";
     static private final String PANELS_TAG = "android:Panels";
+    static private final String ACTION_BAR_TAG = "android:ActionBar";
 
     /** {@inheritDoc} */
     @Override
@@ -1548,6 +1559,10 @@
             outState.putSparseParcelableArray(PANELS_TAG, panelStates);
         }
 
+        if (mActionBar != null) {
+            outState.putBoolean(ACTION_BAR_TAG, mActionBar.isOverflowMenuShowing());
+        }
+
         return outState;
     }
 
@@ -1582,6 +1597,10 @@
         if (panelStates != null) {
             restorePanelState(panelStates);
         }
+
+        if (mActionBar != null && savedInstanceState.getBoolean(ACTION_BAR_TAG)) {
+            mActionBar.postShowOverflowMenu();
+        }
     }
 
     /**
@@ -2353,11 +2372,15 @@
             } else {
                 layoutResource = com.android.internal.R.layout.screen_title_icons;
             }
+            // XXX Remove this once action bar supports these features.
+            removeFeature(FEATURE_ACTION_BAR);
             // System.out.println("Title Icons!");
         } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) {
             // Special case for a window with only a progress bar (and title).
             // XXX Need to have a no-title version of embedded windows.
             layoutResource = com.android.internal.R.layout.screen_progress;
+            // XXX Remove this once action bar supports these features.
+            removeFeature(FEATURE_ACTION_BAR);
             // System.out.println("Progress!");
         } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
             // Special case for a window with a custom title.
@@ -2367,6 +2390,8 @@
             } else {
                 layoutResource = com.android.internal.R.layout.screen_custom_title;
             }
+            // XXX Remove this once action bar supports these features.
+            removeFeature(FEATURE_ACTION_BAR);
         } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
             // If no other features and not embedded, only need a title.
             // If the window is floating, we need a dialog layout
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index a07c385..4d4c799 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -46,6 +46,7 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 
+import com.android.internal.R;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.telephony.ITelephony;
@@ -269,6 +270,7 @@
     Intent mHomeIntent;
     Intent mCarDockIntent;
     Intent mDeskDockIntent;
+    Intent mRecentAppsIntent;
     boolean mSearchKeyPressed;
     boolean mConsumeSearchKeyUp;
 
@@ -491,6 +493,16 @@
      * Create (if necessary) and launch the recent apps dialog
      */
     void showRecentAppsDialog() {
+        if (mRecentAppsIntent != null) {
+            try {
+                mContext.startActivity(mRecentAppsIntent);
+                return;
+            } catch (ActivityNotFoundException e) {
+                Log.e(TAG, "Failed to launch RecentAppsIntent", e);
+            }
+        }
+
+        // Fallback to dialog if we fail to launch the above.
         if (mRecentAppsDialog == null) {
             mRecentAppsDialog = new RecentApplicationsDialog(mContext);
         }
@@ -522,6 +534,18 @@
         mDeskDockIntent.addCategory(Intent.CATEGORY_DESK_DOCK);
         mDeskDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        boolean use3dRecents = mContext.getResources().getBoolean(R.bool.config_enableRecentApps3D);
+        if (use3dRecents) {
+            mRecentAppsIntent = new Intent();
+            mRecentAppsIntent.setClassName("com.android.systemui", 
+                    "com.android.systemui.statusbar.RecentApplicationsActivity");
+            mRecentAppsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        } else {
+            mRecentAppsIntent = null;
+        }
+
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mBroadcastWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                 "PhoneWindowManager.mBroadcastWakeLock");
@@ -2096,9 +2120,7 @@
                 return mDeskDockRotation;
             } else {
                 if (useSensorForOrientationLp(orientation)) {
-                    // If the user has enabled auto rotation by default, do it.
-                    int curRotation = mOrientationListener.getCurrentRotation();
-                    return curRotation >= 0 ? curRotation : lastRotation;
+                    return mOrientationListener.getCurrentRotation(lastRotation);
                 }
                 return Surface.ROTATION_0;
             }
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 6e7633e..ff31470 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1601,7 +1601,7 @@
         }
 
         if (mSuspended) {
-            sleepTime = idleSleepTime;
+            sleepTime = suspendSleepTimeUs();
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
@@ -2021,6 +2021,11 @@
     return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
 }
 
+uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs()
+{
+    return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+}
+
 // ----------------------------------------------------------------------------
 AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
     :   PlaybackThread(audioFlinger, output, id, device)
@@ -2365,7 +2370,7 @@
         }
 
         if (mSuspended) {
-            sleepTime = idleSleepTime;
+            sleepTime = suspendSleepTimeUs();
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
@@ -2486,6 +2491,18 @@
     return time;
 }
 
+uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs()
+{
+    uint32_t time;
+    if (AudioSystem::isLinearPCM(mFormat)) {
+        time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+    } else {
+        time = 10000;
+    }
+    return time;
+}
+
+
 // ----------------------------------------------------------------------------
 
 AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
@@ -2612,7 +2629,7 @@
         }
 
         if (mSuspended) {
-            sleepTime = idleSleepTime;
+            sleepTime = suspendSleepTimeUs();
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 5520551..51881f0 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -664,6 +664,7 @@
         virtual void            deleteTrackName_l(int name) = 0;
         virtual uint32_t        activeSleepTimeUs() = 0;
         virtual uint32_t        idleSleepTimeUs() = 0;
+        virtual uint32_t        suspendSleepTimeUs() = 0;
 
     private:
 
@@ -724,6 +725,7 @@
         virtual     void        deleteTrackName_l(int name);
         virtual     uint32_t    activeSleepTimeUs();
         virtual     uint32_t    idleSleepTimeUs();
+        virtual     uint32_t    suspendSleepTimeUs();
 
         AudioMixer*                     mAudioMixer;
     };
@@ -744,6 +746,7 @@
         virtual     void        deleteTrackName_l(int name);
         virtual     uint32_t    activeSleepTimeUs();
         virtual     uint32_t    idleSleepTimeUs();
+        virtual     uint32_t    suspendSleepTimeUs();
 
     private:
         void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
diff --git a/services/camera/libcameraservice/CameraHardwareStub.cpp b/services/camera/libcameraservice/CameraHardwareStub.cpp
index b3e0ee6..07b5a37 100644
--- a/services/camera/libcameraservice/CameraHardwareStub.cpp
+++ b/services/camera/libcameraservice/CameraHardwareStub.cpp
@@ -101,9 +101,9 @@
     mFakeCamera = 0; // paranoia
 }
 
-sp<IMemoryHeap> CameraHardwareStub::getPreviewHeap() const
+status_t CameraHardwareStub::setPreviewWindow(const sp<ANativeWindow>& buf)
 {
-    return mPreviewHeap;
+    return NO_ERROR;
 }
 
 sp<IMemoryHeap> CameraHardwareStub::getRawHeap() const
diff --git a/services/camera/libcameraservice/CameraHardwareStub.h b/services/camera/libcameraservice/CameraHardwareStub.h
index d3427ba..9b66a76 100644
--- a/services/camera/libcameraservice/CameraHardwareStub.h
+++ b/services/camera/libcameraservice/CameraHardwareStub.h
@@ -29,7 +29,7 @@
 
 class CameraHardwareStub : public CameraHardwareInterface {
 public:
-    virtual sp<IMemoryHeap> getPreviewHeap() const;
+    virtual status_t setPreviewWindow(const sp<ANativeWindow>& buf);
     virtual sp<IMemoryHeap> getRawHeap() const;
 
     virtual void        setCallbacks(notify_callback notify_cb,
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 3b3904a..c6a9909 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -145,7 +145,12 @@
         return NULL;
     }
 
-    client = new Client(this, cameraClient, cameraId, callingPid);
+    sp<CameraHardwareInterface> hardware = HAL_openCameraHardware(cameraId);
+    if (hardware == NULL) {
+        LOGE("Fail to open camera hardware (id=%d)", cameraId);
+        return NULL;
+    }
+    client = new Client(this, cameraClient, hardware, cameraId, callingPid);
     mClient[cameraId] = client;
     LOG1("CameraService::connect X");
     return client;
@@ -285,16 +290,17 @@
 // ----------------------------------------------------------------------------
 
 CameraService::Client::Client(const sp<CameraService>& cameraService,
-        const sp<ICameraClient>& cameraClient, int cameraId, int clientPid) {
+        const sp<ICameraClient>& cameraClient,
+        const sp<CameraHardwareInterface>& hardware,
+        int cameraId, int clientPid) {
     int callingPid = getCallingPid();
     LOG1("Client::Client E (pid %d)", callingPid);
 
     mCameraService = cameraService;
     mCameraClient = cameraClient;
+    mHardware = hardware;
     mCameraId = cameraId;
     mClientPid = clientPid;
-
-    mHardware = HAL_openCameraHardware(cameraId);
     mUseOverlay = mHardware->useOverlay();
     mMsgEnabled = 0;
 
@@ -330,16 +336,9 @@
     int callingPid = getCallingPid();
     LOG1("Client::~Client E (pid %d, this %p)", callingPid, this);
 
-    if (mSurface != 0 && !mUseOverlay) {
-        pthread_t thr;
-        // We unregister the buffers in a different thread because binder does
-        // not let us make sychronous transactions in a binder destructor (that
-        // is, upon our reaching a refcount of zero.)
-        pthread_create(&thr,
-                       NULL,  // attr
-                       unregister_surface,
-                       mSurface.get());
-        pthread_join(thr, NULL);
+    // Clean up the ANativeWindow
+    if (mSurface != 0) {
+        setPreviewDisplay(0);
     }
 
     // set mClientPid to let disconnet() tear down the hardware
@@ -459,6 +458,11 @@
     if (mUseOverlay) {
         mOverlayRef = 0;
     }
+    // Release the held ANativeWindow resources.
+    if (mPreviewWindow != 0) {
+        mPreviewWindow = 0;
+        mHardware->setPreviewWindow(mPreviewWindow);
+    }
     mHardware.clear();
 
     mCameraService->removeClient(mCameraClient);
@@ -469,8 +473,8 @@
 
 // ----------------------------------------------------------------------------
 
-// set the ISurface that the preview will use
-status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) {
+// set the Surface that the preview will use
+status_t CameraService::Client::setPreviewDisplay(const sp<Surface>& surface) {
     LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid());
     Mutex::Autolock lock(mLock);
     status_t result = checkPidAndHardware();
@@ -480,7 +484,7 @@
 
     // return if no change in surface.
     // asBinder() is safe on NULL (returns NULL)
-    if (surface->asBinder() == mSurface->asBinder()) {
+    if (getISurface(surface)->asBinder() == mSurface->asBinder()) {
         return result;
     }
 
@@ -490,44 +494,28 @@
             // Force the destruction of any previous overlay
             sp<Overlay> dummy;
             mHardware->setOverlay(dummy);
-        } else {
-            mSurface->unregisterBuffers();
         }
     }
-    mSurface = surface;
+    if (surface != 0) {
+        mSurface = getISurface(surface);
+    } else {
+        mSurface = 0;
+    }
+    mPreviewWindow = surface;
     mOverlayRef = 0;
     // If preview has been already started, set overlay or register preview
     // buffers now.
     if (mHardware->previewEnabled()) {
         if (mUseOverlay) {
             result = setOverlay();
-        } else if (mSurface != 0) {
-            result = registerPreviewBuffers();
+        } else if (mPreviewWindow != 0) {
+            result = mHardware->setPreviewWindow(mPreviewWindow);
         }
     }
 
     return result;
 }
 
-status_t CameraService::Client::registerPreviewBuffers() {
-    int w, h;
-    CameraParameters params(mHardware->getParameters());
-    params.getPreviewSize(&w, &h);
-
-    // FIXME: don't use a hardcoded format here.
-    ISurface::BufferHeap buffers(w, h, w, h,
-                                 HAL_PIXEL_FORMAT_YCrCb_420_SP,
-                                 mOrientation,
-                                 0,
-                                 mHardware->getPreviewHeap());
-
-    status_t result = mSurface->registerBuffers(buffers);
-    if (result != NO_ERROR) {
-        LOGE("registerBuffers failed with status %d", result);
-    }
-    return result;
-}
-
 status_t CameraService::Client::setOverlay() {
     int w, h;
     CameraParameters params(mHardware->getParameters());
@@ -618,14 +606,14 @@
 
     switch(mode) {
         case CAMERA_PREVIEW_MODE:
-            if (mSurface == 0) {
+            if (mSurface == 0 && mPreviewWindow == 0) {
                 LOG1("mSurface is not set yet.");
                 // still able to start preview in this case.
             }
             return startPreviewMode();
         case CAMERA_RECORDING_MODE:
-            if (mSurface == 0) {
-                LOGE("mSurface must be set before startRecordingMode.");
+            if (mSurface == 0 && mPreviewWindow == 0) {
+                LOGE("mSurface or mPreviewWindow must be set before startRecordingMode.");
                 return INVALID_OPERATION;
             }
             return startRecordingMode();
@@ -651,16 +639,10 @@
         if (result != NO_ERROR) return result;
         result = mHardware->startPreview();
     } else {
+        // XXX: Set the orientation of the ANativeWindow.
+        mHardware->setPreviewWindow(mPreviewWindow);
         enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
         result = mHardware->startPreview();
-        if (result != NO_ERROR) return result;
-        // If preview display has been set, register preview buffers now.
-        if (mSurface != 0) {
-           // Unregister here because the surface may be previously registered
-           // with the raw (snapshot) heap.
-           mSurface->unregisterBuffers();
-           result = registerPreviewBuffers();
-        }
     }
     return result;
 }
@@ -698,13 +680,10 @@
     Mutex::Autolock lock(mLock);
     if (checkPidAndHardware() != NO_ERROR) return;
 
+
     disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
     mHardware->stopPreview();
 
-    if (mSurface != 0 && !mUseOverlay) {
-        mSurface->unregisterBuffers();
-    }
-
     mPreviewBuffer.clear();
 }
 
@@ -992,11 +971,6 @@
 void CameraService::Client::handleShutter(image_rect_type *size) {
     mCameraService->playSound(SOUND_SHUTTER);
 
-    // Screen goes black after the buffer is unregistered.
-    if (mSurface != 0 && !mUseOverlay) {
-        mSurface->unregisterBuffers();
-    }
-
     sp<ICameraClient> c = mCameraClient;
     if (c != 0) {
         mLock.unlock();
@@ -1024,7 +998,6 @@
             HAL_PIXEL_FORMAT_YCrCb_420_SP, mOrientation, 0,
             mHardware->getRawHeap());
 
-        mSurface->registerBuffers(buffers);
         IPCThreadState::self()->flushCommands();
     }
 
@@ -1037,12 +1010,6 @@
     size_t size;
     sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
 
-    if (!mUseOverlay) {
-        if (mSurface != 0) {
-            mSurface->postBuffer(offset);
-        }
-    }
-
     // local copy of the callback flags
     int flags = mPreviewCallbackFlag;
 
@@ -1102,11 +1069,6 @@
     size_t size;
     sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
 
-    // Put the YUV version of the snapshot in the preview display.
-    if (mSurface != 0 && !mUseOverlay) {
-        mSurface->postBuffer(offset);
-    }
-
     sp<ICameraClient> c = mCameraClient;
     mLock.unlock();
     if (c != 0) {
@@ -1264,4 +1226,12 @@
     return NO_ERROR;
 }
 
+sp<ISurface> CameraService::getISurface(const sp<Surface>& surface) {
+    if (surface != 0) {
+        return surface->getISurface();
+    } else {
+        return sp<ISurface>(0);
+    }
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 77ccf41..7ed192e 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -79,6 +79,12 @@
     sp<MediaPlayer>     mSoundPlayer[NUM_SOUNDS];
     int                 mSoundRef;  // reference count (release all MediaPlayer when 0)
 
+    // Used by Client objects to extract the ISurface from a Surface object.
+    // This is used because making Client a friend class of Surface would
+    // require including this header in Surface.h since Client is a nested
+    // class.
+    static sp<ISurface> getISurface(const sp<Surface>& surface);
+
     class Client : public BnCamera
     {
     public:
@@ -87,7 +93,7 @@
         virtual status_t        connect(const sp<ICameraClient>& client);
         virtual status_t        lock();
         virtual status_t        unlock();
-        virtual status_t        setPreviewDisplay(const sp<ISurface>& surface);
+        virtual status_t        setPreviewDisplay(const sp<Surface>& surface);
         virtual void            setPreviewCallbackFlag(int flag);
         virtual status_t        startPreview();
         virtual void            stopPreview();
@@ -106,6 +112,7 @@
         friend class CameraService;
                                 Client(const sp<CameraService>& cameraService,
                                        const sp<ICameraClient>& cameraClient,
+                                       const sp<CameraHardwareInterface>& hardware,
                                        int cameraId,
                                        int clientPid);
                                 ~Client();
@@ -168,6 +175,7 @@
         // Ensures atomicity among the public methods
         mutable Mutex                   mLock;
         sp<ISurface>                    mSurface;
+        sp<ANativeWindow>               mPreviewWindow;
 
         // If the user want us to return a copy of the preview frame (instead
         // of the original one), we allocate mPreviewBuffer and reuse it if possible.
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index f825df9..5ef3d35 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.ResolveInfo;
@@ -1161,7 +1162,7 @@
                 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                     added = true;
-                } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                     added = false;
                 } else  {
@@ -1222,7 +1223,9 @@
         for (int i=0; i<N; i++) {
             ResolveInfo ri = broadcastReceivers.get(i);
             ActivityInfo ai = ri.activityInfo;
-            
+            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+                continue;
+            }
             if (pkgName.equals(ai.packageName)) {
                 addProviderLocked(ri);
             }
@@ -1241,6 +1244,9 @@
         for (int i=0; i<N; i++) {
             ResolveInfo ri = broadcastReceivers.get(i);
             ActivityInfo ai = ri.activityInfo;
+            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+                continue;
+            }
             if (pkgName.equals(ai.packageName)) {
                 ComponentName component = new ComponentName(ai.packageName, ai.name);
                 Provider p = lookupProviderLocked(component);
diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java
index 4e4fc0c..308c9c0 100644
--- a/services/java/com/android/server/ClipboardService.java
+++ b/services/java/com/android/server/ClipboardService.java
@@ -16,7 +16,8 @@
 
 package com.android.server;
 
-import android.content.ClippedData;
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.IClipboard;
 import android.content.IOnPrimaryClipChangedListener;
 import android.content.Context;
@@ -27,7 +28,7 @@
  * Implementation of the clipboard for copy and paste.
  */
 public class ClipboardService extends IClipboard.Stub {
-    private ClippedData mPrimaryClip;
+    private ClipData mPrimaryClip;
     private final RemoteCallbackList<IOnPrimaryClipChangedListener> mPrimaryClipListeners
             = new RemoteCallbackList<IOnPrimaryClipChangedListener>();
 
@@ -36,7 +37,7 @@
      */
     public ClipboardService(Context context) { }
 
-    public void setPrimaryClip(ClippedData clip) {
+    public void setPrimaryClip(ClipData clip) {
         synchronized (this) {
             if (clip != null && clip.getItemCount() <= 0) {
                 throw new IllegalArgumentException("No items");
@@ -56,12 +57,18 @@
         }
     }
     
-    public ClippedData getPrimaryClip() {
+    public ClipData getPrimaryClip() {
         synchronized (this) {
             return mPrimaryClip;
         }
     }
 
+    public ClipDescription getPrimaryClipDescription() {
+        synchronized (this) {
+            return new ClipDescription(mPrimaryClip);
+        }
+    }
+
     public boolean hasPrimaryClip() {
         synchronized (this) {
             return mPrimaryClip != null;
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 4da0bac..bd2a0eaa6 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -943,7 +943,6 @@
         }
 
         Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
         if (info.isFailover()) {
             intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -1059,7 +1058,6 @@
 
     private void sendConnectedBroadcast(NetworkInfo info) {
         Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
         if (info.isFailover()) {
             intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -1097,7 +1095,6 @@
         }
 
         Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
         if (getActiveNetworkInfo() == null) {
             intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index c2c799b..f330d40 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -26,6 +26,7 @@
 import android.os.Environment;
 import android.os.LocalPowerManager;
 import android.os.PowerManager;
+import android.os.SystemProperties;
 import android.util.Slog;
 import android.util.Xml;
 import android.view.InputChannel;
@@ -47,9 +48,6 @@
 
 /*
  * Wraps the C++ InputManager and provides its callbacks.
- * 
- * XXX Tempted to promote this to a first-class service, ie. InputManagerService, to
- *     improve separation of concerns with respect to the window manager.
  */
 public class InputManager {
     static final String TAG = "InputManager";
@@ -507,5 +505,18 @@
             
             return names.toArray(new String[names.size()]);
         }
+        
+        @SuppressWarnings("unused")
+        public int getMaxEventsPerSecond() {
+            int result = 0;
+            try {
+                result = Integer.parseInt(SystemProperties.get("windowsmgr.max_events_per_sec"));
+            } catch (NumberFormatException e) {
+            }
+            if (result < 1) {
+                result = 60;
+            }
+            return result;
+        }
     }
 }
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 4ee89cc..16f3f10 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -1043,11 +1043,10 @@
     // If they gave a timeoutOverride it is the number of seconds
     // to screen-off.  Figure out where in the countdown cycle we
     // should jump to.
-    private void setTimeoutLocked(long now, long timeoutOverride, int nextState) {
+    private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) {
+        long timeoutOverride = originalTimeoutOverride;
         if (mBootCompleted) {
             synchronized (mLocks) {
-                mHandler.removeCallbacks(mTimeoutTask);
-                mTimeoutTask.nextState = nextState;
                 long when = 0;
                 if (timeoutOverride <= 0) {
                     switch (nextState)
@@ -1098,6 +1097,12 @@
                             + " timeoutOverride=" + timeoutOverride
                             + " nextState=" + nextState + " when=" + when);
                 }
+
+                mHandler.removeCallbacks(mTimeoutTask);
+                mTimeoutTask.nextState = nextState;
+                mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0
+                        ? (originalTimeoutOverride - timeoutOverride)
+                        : -1;
                 mHandler.postAtTime(mTimeoutTask, when);
                 mNextTimeout = when; // for debugging
             }
@@ -1113,6 +1118,7 @@
     private class TimeoutTask implements Runnable
     {
         int nextState; // access should be synchronized on mLocks
+        long remainingTimeoutOverride;
         public void run()
         {
             synchronized (mLocks) {
@@ -1133,11 +1139,11 @@
                 {
                     case SCREEN_BRIGHT:
                         if (mDimDelay >= 0) {
-                            setTimeoutLocked(now, SCREEN_DIM);
+                            setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM);
                             break;
                         }
                     case SCREEN_DIM:
-                        setTimeoutLocked(now, SCREEN_OFF);
+                        setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF);
                         break;
                 }
             }
@@ -2076,6 +2082,7 @@
                         + " mUserState=0x" + Integer.toHexString(mUserState)
                         + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)
                         + " mProximitySensorActive=" + mProximitySensorActive
+                        + " timeoutOverride=" + timeoutOverride
                         + " force=" + force);
             }
             // ignore user activity if we are in the process of turning off the screen
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 9f8557f..6ecc511 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -357,7 +357,7 @@
      */
     public boolean pingSupplicant() {
         enforceAccessPermission();
-        return mWifiStateMachine.pingSupplicant();
+        return mWifiStateMachine.syncPingSupplicant();
     }
 
     /**
@@ -436,7 +436,7 @@
      */
     public int getWifiEnabledState() {
         enforceAccessPermission();
-        return mWifiStateMachine.getWifiState();
+        return mWifiStateMachine.syncGetWifiState();
     }
 
     /**
@@ -481,7 +481,7 @@
      */
     public int getWifiApEnabledState() {
         enforceAccessPermission();
-        return mWifiStateMachine.getWifiApState();
+        return mWifiStateMachine.syncGetWifiApState();
     }
 
     /**
@@ -555,7 +555,7 @@
      */
     public List<WifiConfiguration> getConfiguredNetworks() {
         enforceAccessPermission();
-        return mWifiStateMachine.getConfiguredNetworks();
+        return mWifiStateMachine.syncGetConfiguredNetworks();
     }
 
     /**
@@ -565,7 +565,7 @@
      */
     public int addOrUpdateNetwork(WifiConfiguration config) {
         enforceChangePermission();
-        return mWifiStateMachine.addOrUpdateNetwork(config);
+        return mWifiStateMachine.syncAddOrUpdateNetwork(config);
     }
 
      /**
@@ -576,7 +576,7 @@
      */
     public boolean removeNetwork(int netId) {
         enforceChangePermission();
-        return mWifiStateMachine.removeNetwork(netId);
+        return mWifiStateMachine.syncRemoveNetwork(netId);
     }
 
     /**
@@ -588,7 +588,7 @@
      */
     public boolean enableNetwork(int netId, boolean disableOthers) {
         enforceChangePermission();
-        return mWifiStateMachine.enableNetwork(netId, disableOthers);
+        return mWifiStateMachine.syncEnableNetwork(netId, disableOthers);
     }
 
     /**
@@ -599,7 +599,7 @@
      */
     public boolean disableNetwork(int netId) {
         enforceChangePermission();
-        return mWifiStateMachine.disableNetwork(netId);
+        return mWifiStateMachine.syncDisableNetwork(netId);
     }
 
     /**
@@ -612,7 +612,7 @@
          * Make sure we have the latest information, by sending
          * a status request to the supplicant.
          */
-        return mWifiStateMachine.requestConnectionInfo();
+        return mWifiStateMachine.syncRequestConnectionInfo();
     }
 
     /**
@@ -622,7 +622,7 @@
      */
     public List<ScanResult> getScanResults() {
         enforceAccessPermission();
-        return mWifiStateMachine.getScanResultsList();
+        return mWifiStateMachine.syncGetScanResultsList();
     }
 
     /**
@@ -634,7 +634,7 @@
     public boolean saveConfiguration() {
         boolean result = true;
         enforceChangePermission();
-        return mWifiStateMachine.saveConfig();
+        return mWifiStateMachine.syncSaveConfig();
     }
 
     /**
@@ -723,7 +723,7 @@
      */
     public DhcpInfo getDhcpInfo() {
         enforceAccessPermission();
-        return mWifiStateMachine.getDhcpInfo();
+        return mWifiStateMachine.syncGetDhcpInfo();
     }
 
     /**
@@ -823,7 +823,7 @@
                  * or plugged in to AC).
                  */
                 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
-                    WifiInfo info = mWifiStateMachine.requestConnectionInfo();
+                    WifiInfo info = mWifiStateMachine.syncRequestConnectionInfo();
                     if (info.getSupplicantState() != SupplicantState.COMPLETED) {
                         // we used to go to sleep immediately, but this caused some race conditions
                         // we don't have time to track down for this release.  Delay instead,
@@ -913,7 +913,7 @@
          * of {@code 0} isn't really a plugged type, but rather an indication that the
          * device isn't plugged in at all, there is no bit value corresponding to a
          * {@code pluggedType} value of {@code 0}. That is why we shift by
-         * {@code pluggedType&nbsp;&#8212;&nbsp;1} instead of by {@code pluggedType}.
+         * {@code pluggedType - 1} instead of by {@code pluggedType}.
          * @param stayAwakeConditions a bit string specifying which "plugged types" should
          * keep the device (and hence Wi-Fi) awake.
          * @param pluggedType the type of plug (USB, AC, or none) for which the check is
@@ -1002,7 +1002,7 @@
                     + ", uid=" + Binder.getCallingUid());
             return;
         }
-        pw.println("Wi-Fi is " + mWifiStateMachine.getWifiStateByName());
+        pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
         pw.println("Stay-awake conditions: " +
                 Settings.System.getInt(mContext.getContentResolver(),
                                        Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
@@ -1012,7 +1012,7 @@
         pw.println(mWifiStateMachine);
         pw.println();
         pw.println("Latest scan results:");
-        List<ScanResult> scanResults = mWifiStateMachine.getScanResultsList();
+        List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
         if (scanResults != null && scanResults.size() != 0) {
             pw.println("  BSSID              Frequency   RSSI  Flags             SSID");
             for (ScanResult r : scanResults) {
@@ -1320,7 +1320,7 @@
         if ((state == NetworkInfo.State.DISCONNECTED)
                 || (state == NetworkInfo.State.UNKNOWN)) {
             // Look for an open network
-            List<ScanResult> scanResults = mWifiStateMachine.getScanResultsList();
+            List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
             if (scanResults != null) {
                 int numOpenNetworks = 0;
                 for (int i = scanResults.size() - 1; i >= 0; i--) {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 57f93c4..4407e96 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -16,11 +16,6 @@
 
 package com.android.server;
 
-import static android.os.LocalPowerManager.CHEEK_EVENT;
-import static android.os.LocalPowerManager.OTHER_EVENT;
-import static android.os.LocalPowerManager.TOUCH_EVENT;
-import static android.os.LocalPowerManager.LONG_TOUCH_EVENT;
-import static android.os.LocalPowerManager.TOUCH_UP_EVENT;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
@@ -76,7 +71,6 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.LatencyTimer;
 import android.os.LocalPowerManager;
 import android.os.Looper;
 import android.os.Message;
@@ -109,13 +103,11 @@
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
-import android.view.InputQueue;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.SurfaceSession;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
@@ -127,9 +119,13 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Transformation;
 
+import java.io.BufferedReader;
 import java.io.BufferedWriter;
+import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
@@ -164,8 +160,6 @@
     static final boolean DEBUG_WALLPAPER = false;
     static final boolean SHOW_TRANSACTIONS = false;
     static final boolean HIDE_STACK_CRAWLS = true;
-    static final boolean MEASURE_LATENCY = false;
-    static private LatencyTimer lt;
 
     static final boolean PROFILE_ORIENTATION = false;
     static final boolean BLUR = true;
@@ -603,10 +597,6 @@
 
     private WindowManagerService(Context context, PowerManagerService pm,
             boolean haveInputMethods) {
-        if (MEASURE_LATENCY) {
-            lt = new LatencyTimer(100, 1000);
-        }
-
         mContext = context;
         mHaveInputMethods = haveInputMethods;
         mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -4808,6 +4798,8 @@
         Parcel data = null;
         Parcel reply = null;
 
+        BufferedWriter out = null;
+
         // Any uncaught exception will crash the system process
         try {
             // Find the hashcode of the window
@@ -4845,6 +4837,12 @@
 
             reply.readException();
 
+            if (!client.isOutputShutdown()) {
+                out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
+                out.write("DONE\n");
+                out.flush();
+            }
+
         } catch (Exception e) {
             Slog.w(TAG, "Could not send command " + command + " with parameters " + parameters, e);
             success = false;
@@ -4855,6 +4853,13 @@
             if (reply != null) {
                 reply.recycle();
             }
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+
+                }
+            }
         }
 
         return success;
@@ -9582,6 +9587,10 @@
         
         Surface.closeTransaction();
 
+        if (mWatermark != null) {
+            mWatermark.drawIfNeeded();
+        }
+
         if (DEBUG_ORIENTATION && mDisplayFrozen) Slog.v(TAG,
                 "With display frozen, orientationChangeComplete="
                 + orientationChangeComplete);
@@ -10067,12 +10076,17 @@
         mScreenFrozenLock.release();
     }
 
-    static int getPropertyInt(String name, int defUnits, int defDps, DisplayMetrics dm) {
-        String str = SystemProperties.get(name);
-        try {
-            int val = Integer.parseInt(str);
-            return val;
-        } catch (Exception e) {
+    static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps,
+            DisplayMetrics dm) {
+        if (index < tokens.length) {
+            String str = tokens[index];
+            if (str != null && str.length() > 0) {
+                try {
+                    int val = Integer.parseInt(str);
+                    return val;
+                } catch (Exception e) {
+                }
+            }
         }
         if (defUnits == TypedValue.COMPLEX_UNIT_PX) {
             return defDps;
@@ -10082,62 +10096,142 @@
     }
 
     class Watermark {
-        Surface mSurface;
-        int mWidth;
-        int mHeight;
-        int mXPercent;
-        int mYPercent;
+        final String[] mTokens;
+        final String mText;
+        final Paint mTextPaint;
+        final int mTextWidth;
+        final int mTextHeight;
+        final int mTextAscent;
+        final int mTextDescent;
+        final int mDeltaX;
+        final int mDeltaY;
 
-        Watermark(SurfaceSession session, String text) {
+        Surface mSurface;
+        int mLastDW;
+        int mLastDH;
+        boolean mDrawNeeded;
+
+        Watermark(SurfaceSession session, String[] tokens) {
             final DisplayMetrics dm = new DisplayMetrics();
             mDisplay.getMetrics(dm);
 
-            int fontSize = getPropertyInt("ro.watermark.height",
-                    TypedValue.COMPLEX_UNIT_DIP, 48, dm);
-            mXPercent = getPropertyInt("ro.watermark.x",
-                    TypedValue.COMPLEX_UNIT_PX, 50, dm);
-            mYPercent = getPropertyInt("ro.watermark.y",
-                    TypedValue.COMPLEX_UNIT_PX, 99, dm);
-            int color = getPropertyInt("ro.watermark.color",
-                    TypedValue.COMPLEX_UNIT_PX, 0x80ffffff, dm);
-            int shadowRadius = getPropertyInt("ro.watermark.shadow.radius",
-                    TypedValue.COMPLEX_UNIT_PX, 5, dm);
-            int shadowDx = getPropertyInt("ro.watermark.shadow.dx",
-                    TypedValue.COMPLEX_UNIT_PX, 0, dm);
-            int shadowDy = getPropertyInt("ro.watermark.shadow.dy",
-                    TypedValue.COMPLEX_UNIT_PX, 0, dm);
-            int shadowColor = getPropertyInt("ro.watermark.shadow.color",
-                    TypedValue.COMPLEX_UNIT_PX, 0xff000000, dm);
+            if (false) {
+                Log.i(TAG, "*********************** WATERMARK");
+                for (int i=0; i<tokens.length; i++) {
+                    Log.i(TAG, "  TOKEN #" + i + ": " + tokens[i]);
+                }
+            }
 
-            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
-            paint.setTextSize(fontSize);
-            paint.setColor(color);
-            paint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
-            paint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
+            mTokens = tokens;
 
-            FontMetricsInt fm = paint.getFontMetricsInt();
-            mHeight = fm.descent - fm.ascent + 20;
-            mWidth = (int)paint.measureText(text) + 20;
+            StringBuilder builder = new StringBuilder(32);
+            int len = mTokens[0].length();
+            len = len & ~1;
+            for (int i=0; i<len; i+=2) {
+                int c1 = mTokens[0].charAt(i);
+                int c2 = mTokens[0].charAt(i+1);
+                if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + 10;
+                else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + 10;
+                else c1 -= '0';
+                if (c2 >= 'a' && c2 <= 'f') c2 = c2 - 'a' + 10;
+                else if (c2 >= 'A' && c2 <= 'F') c2 = c2 - 'A' + 10;
+                else c2 -= '0';
+                builder.append((char)(255-((c1*16)+c2)));
+            }
+            mText = builder.toString();
+            if (false) {
+                Log.i(TAG, "Final text: " + mText);
+            }
+
+            int fontSize = getPropertyInt(tokens, 1,
+                    TypedValue.COMPLEX_UNIT_DIP, 20, dm);
+
+            mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            mTextPaint.setTextSize(fontSize);
+            mTextPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
+
+            FontMetricsInt fm = mTextPaint.getFontMetricsInt();
+            mTextWidth = (int)mTextPaint.measureText(mText);
+            mTextAscent = fm.ascent;
+            mTextDescent = fm.descent;
+            mTextHeight = fm.descent - fm.ascent;
+
+            mDeltaX = getPropertyInt(tokens, 2,
+                    TypedValue.COMPLEX_UNIT_PX, mTextWidth*2, dm);
+            mDeltaY = getPropertyInt(tokens, 3,
+                    TypedValue.COMPLEX_UNIT_PX, mTextHeight*3, dm);
+            int shadowColor = getPropertyInt(tokens, 4,
+                    TypedValue.COMPLEX_UNIT_PX, 0xb0000000, dm);
+            int color = getPropertyInt(tokens, 5,
+                    TypedValue.COMPLEX_UNIT_PX, 0x60ffffff, dm);
+            int shadowRadius = getPropertyInt(tokens, 6,
+                    TypedValue.COMPLEX_UNIT_PX, 7, dm);
+            int shadowDx = getPropertyInt(tokens, 8,
+                    TypedValue.COMPLEX_UNIT_PX, 0, dm);
+            int shadowDy = getPropertyInt(tokens, 9,
+                    TypedValue.COMPLEX_UNIT_PX, 0, dm);
+
+            mTextPaint.setColor(color);
+            mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
 
             try {
                 mSurface = new Surface(session, 0,
-                        "WatermarkSurface",
-                        -1, mWidth, mHeight, PixelFormat.TRANSLUCENT, 0);
+                        "WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
                 mSurface.setLayer(TYPE_LAYER_MULTIPLIER*100);
-                Rect dirty = new Rect(0, 0, mWidth, mHeight);
-                Canvas c = mSurface.lockCanvas(dirty);
-                c.drawText(text, 10, -fm.ascent+10, paint);
-                mSurface.unlockCanvasAndPost(c);
+                mSurface.setPosition(0, 0);
                 mSurface.show();
             } catch (OutOfResourcesException e) {
             }
         }
 
         void positionSurface(int dw, int dh) {
-            int availW = dw - mWidth;
-            int availH = dh - mHeight;
-            mSurface.setPosition((availW*mXPercent)/100,
-                    (availH*mYPercent)/100);
+            if (mLastDW != dw || mLastDH != dh) {
+                mLastDW = dw;
+                mLastDH = dh;
+                mSurface.setSize(dw, dh);
+                mDrawNeeded = true;
+            }
+        }
+
+        void drawIfNeeded() {
+            if (mDrawNeeded) {
+                final int dw = mLastDW;
+                final int dh = mLastDH;
+
+                mDrawNeeded = false;
+                Rect dirty = new Rect(0, 0, dw, dh);
+                Canvas c = null;
+                try {
+                    c = mSurface.lockCanvas(dirty);
+                } catch (IllegalArgumentException e) {
+                } catch (OutOfResourcesException e) {
+                }
+                if (c != null) {
+                    int deltaX = mDeltaX;
+                    int deltaY = mDeltaY;
+
+                    // deltaX shouldn't be close to a round fraction of our
+                    // x step, or else things will line up too much.
+                    int div = (dw+mTextWidth)/deltaX;
+                    int rem = (dw+mTextWidth) - (div*deltaX);
+                    int qdelta = deltaX/4;
+                    if (rem < qdelta || rem > (deltaX-qdelta)) {
+                        deltaX += deltaX/3;
+                    }
+
+                    int y = -mTextHeight;
+                    int x = -mTextWidth;
+                    while (y < (dh+mTextHeight)) {
+                        c.drawText(mText, x, y, mTextPaint);
+                        x += deltaX;
+                        if (x >= dw) {
+                            x -= (dw+mTextWidth);
+                            y += deltaY;
+                        }
+                    }
+                    mSurface.unlockCanvasAndPost(c);
+                }
+            }
         }
     }
 
@@ -10146,12 +10240,28 @@
             return;
         }
 
-        String text = SystemProperties.get("ro.watermark.text");
-        if (text == null || text.length() <= 0) {
-            return;
+        File file = new File("/system/etc/setup.conf");
+        FileInputStream in = null;
+        try {
+            in = new FileInputStream(file);
+            DataInputStream ind = new DataInputStream(in);
+            String line = ind.readLine();
+            if (line != null) {
+                String[] toks = line.split("%");
+                if (toks != null && toks.length > 0) {
+                    mWatermark = new Watermark(mFxSession, toks);
+                }
+            }
+        } catch (FileNotFoundException e) {
+        } catch (IOException e) {
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                }
+            }
         }
-
-        mWatermark = new Watermark(mFxSession, text);
     }
 
     @Override
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 55ec6aa..123dc3e 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -610,8 +610,8 @@
      * All currently bound service connections.  Keys are the IBinder of
      * the client's IServiceConnection.
      */
-    final HashMap<IBinder, ConnectionRecord> mServiceConnections
-            = new HashMap<IBinder, ConnectionRecord>();
+    final HashMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections
+            = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
 
     /**
      * List of services that we have been asked to start,
@@ -1163,7 +1163,7 @@
                     } catch (RemoteException e) {
                     }
                 } catch (NameNotFoundException e) {
-                    Log.w(TAG, "Unable to create context for heavy notification", e);
+                    Slog.w(TAG, "Unable to create context for heavy notification", e);
                 }
             } break;
             case CANCEL_HEAVY_NOTIFICATION_MSG: {
@@ -2368,7 +2368,7 @@
             }
             
             if (proc == null) {
-                Log.w(TAG, "crashApplication: nothing for uid=" + uid
+                Slog.w(TAG, "crashApplication: nothing for uid=" + uid
                         + " initialPid=" + initialPid
                         + " packageName=" + packageName);
                 return;
@@ -4058,6 +4058,9 @@
                     return false;
                 }
             }
+            if (!pi.exported && pi.applicationInfo.uid != uid) {
+                return false;
+            }
             return true;
         } catch (RemoteException e) {
             return false;
@@ -4097,16 +4100,23 @@
         }
     }
 
-    void grantUriPermissionLocked(int callingUid,
-            String targetPkg, Uri uri, int modeFlags, ActivityRecord activity) {
+    /**
+     * Check if the targetPkg can be granted permission to access uri by
+     * the callingUid using the given modeFlags.  Throws a security exception
+     * if callingUid is not allowed to do this.  Returns the uid of the target
+     * if the URI permission grant should be performed; returns -1 if it is not
+     * needed (for example targetPkg already has permission to access the URI).
+     */
+    int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
+            Uri uri, int modeFlags) {
         modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
                 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
         if (modeFlags == 0) {
-            return;
+            return -1;
         }
 
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                "Requested grant " + targetPkg + " permission to " + uri);
+                "Checking grant " + targetPkg + " permission to " + uri);
         
         final IPackageManager pm = AppGlobals.getPackageManager();
 
@@ -4114,7 +4124,7 @@
         if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
             if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                     "Can't grant URI permission for non-content URI: " + uri);
-            return;
+            return -1;
         }
 
         String name = uri.getAuthority();
@@ -4131,7 +4141,7 @@
         }
         if (pi == null) {
             Slog.w(TAG, "No content provider found for: " + name);
-            return;
+            return -1;
         }
 
         int targetUid;
@@ -4140,10 +4150,10 @@
             if (targetUid < 0) {
                 if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                         "Can't grant URI permission no uid for: " + targetPkg);
-                return;
+                return -1;
             }
         } catch (RemoteException ex) {
-            return;
+            return -1;
         }
 
         // First...  does the target actually need this permission?
@@ -4151,7 +4161,7 @@
             // No need to grant the target this permission.
             if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                     "Target " + targetPkg + " already has full permission to " + uri);
-            return;
+            return -1;
         }
 
         // Second...  is the provider allowing granting of URI permissions?
@@ -4188,12 +4198,23 @@
             }
         }
 
-        // Okay!  So here we are: the caller has the assumed permission
+        return targetUid;
+    }
+
+    void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg,
+            Uri uri, int modeFlags, UriPermissionOwner owner) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        if (modeFlags == 0) {
+            return;
+        }
+
+        // So here we are: the caller has the assumed permission
         // to the uri, and the target doesn't.  Let's now give this to
         // the target.
 
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                "Granting " + targetPkg + " permission to " + uri);
+                "Granting " + targetPkg + "/" + targetUid + " permission to " + uri);
         
         HashMap<Uri, UriPermission> targetUris
                 = mGrantedUriPermissions.get(targetUid);
@@ -4206,37 +4227,68 @@
         if (perm == null) {
             perm = new UriPermission(targetUid, uri);
             targetUris.put(uri, perm);
-
         }
+
         perm.modeFlags |= modeFlags;
-        if (activity == null) {
+        if (owner == null) {
             perm.globalModeFlags |= modeFlags;
         } else if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-            perm.readActivities.add(activity);
-            if (activity.readUriPermissions == null) {
-                activity.readUriPermissions = new HashSet<UriPermission>();
-            }
-            activity.readUriPermissions.add(perm);
+            perm.readOwners.add(owner);
+            owner.addReadPermission(perm);
         } else if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-            perm.writeActivities.add(activity);
-            if (activity.writeUriPermissions == null) {
-                activity.writeUriPermissions = new HashSet<UriPermission>();
-            }
-            activity.writeUriPermissions.add(perm);
+            perm.writeOwners.add(owner);
+            owner.addWritePermission(perm);
         }
     }
 
-    void grantUriPermissionFromIntentLocked(int callingUid,
-            String targetPkg, Intent intent, ActivityRecord activity) {
-        if (intent == null) {
+    void grantUriPermissionLocked(int callingUid,
+            String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
+        int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
+        if (targetUid < 0) {
             return;
         }
+
+        grantUriPermissionUncheckedLocked(targetUid, targetPkg, uri, modeFlags, owner);
+    }
+
+    /**
+     * Like checkGrantUriPermissionLocked, but takes an Intent.
+     */
+    int checkGrantUriPermissionFromIntentLocked(int callingUid,
+            String targetPkg, Intent intent) {
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                "Checking URI perm to " + (intent != null ? intent.getData() : null)
+                + " from " + intent + "; flags=0x"
+                + Integer.toHexString(intent != null ? intent.getFlags() : 0));
+
+        if (intent == null) {
+            return -1;
+        }
         Uri data = intent.getData();
         if (data == null) {
+            return -1;
+        }
+        return checkGrantUriPermissionLocked(callingUid, targetPkg, data,
+                intent.getFlags());
+    }
+
+    /**
+     * Like grantUriPermissionUncheckedLocked, but takes an Intent.
+     */
+    void grantUriPermissionUncheckedFromIntentLocked(int targetUid,
+            String targetPkg, Intent intent, UriPermissionOwner owner) {
+        grantUriPermissionUncheckedLocked(targetUid, targetPkg, intent.getData(),
+                intent.getFlags(), owner);
+    }
+
+    void grantUriPermissionFromIntentLocked(int callingUid,
+            String targetPkg, Intent intent, UriPermissionOwner owner) {
+        int targetUid = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg, intent);
+        if (targetUid < 0) {
             return;
         }
-        grantUriPermissionLocked(callingUid, targetPkg, data,
-                intent.getFlags(), activity);
+
+        grantUriPermissionUncheckedFromIntentLocked(targetUid, targetPkg, intent, owner);
     }
 
     public void grantUriPermission(IApplicationThread caller, String targetPkg,
@@ -4249,12 +4301,10 @@
                         + " when granting permission to uri " + uri);
             }
             if (targetPkg == null) {
-                Slog.w(TAG, "grantUriPermission: null target");
-                return;
+                throw new IllegalArgumentException("null target");
             }
             if (uri == null) {
-                Slog.w(TAG, "grantUriPermission: null uri");
-                return;
+                throw new IllegalArgumentException("null uri");
             }
 
             grantUriPermissionLocked(r.info.uid, targetPkg, uri, modeFlags,
@@ -4406,6 +4456,56 @@
         }
     }
 
+    @Override
+    public IBinder newUriPermissionOwner(String name) {
+        synchronized(this) {
+            UriPermissionOwner owner = new UriPermissionOwner(this, name);
+            return owner.getExternalTokenLocked();
+        }
+    }
+
+    @Override
+    public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg,
+            Uri uri, int modeFlags) {
+        synchronized(this) {
+            UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+            if (owner == null) {
+                throw new IllegalArgumentException("Unknown owner: " + token);
+            }
+            if (fromUid != Binder.getCallingUid()) {
+                if (Binder.getCallingUid() != Process.myUid()) {
+                    // Only system code can grant URI permissions on behalf
+                    // of other users.
+                    throw new SecurityException("nice try");
+                }
+            }
+            if (targetPkg == null) {
+                throw new IllegalArgumentException("null target");
+            }
+            if (uri == null) {
+                throw new IllegalArgumentException("null uri");
+            }
+
+            grantUriPermissionLocked(fromUid, targetPkg, uri, modeFlags, owner);
+        }
+    }
+
+    @Override
+    public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode) {
+        synchronized(this) {
+            UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+            if (owner == null) {
+                throw new IllegalArgumentException("Unknown owner: " + token);
+            }
+
+            if (uri == null) {
+                owner.removeUriPermissionsLocked(mode);
+            } else {
+                owner.removeUriPermissionLocked(uri, mode);
+            }
+        }
+    }
+
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
         synchronized (this) {
             ProcessRecord app =
@@ -4906,13 +5006,12 @@
     }
 
     private final String checkContentProviderPermissionLocked(
-            ProviderInfo cpi, ProcessRecord r, int mode) {
+            ProviderInfo cpi, ProcessRecord r) {
         final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
         final int callingUid = (r != null) ? r.info.uid : Binder.getCallingUid();
         if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
                 cpi.exported ? -1 : cpi.applicationInfo.uid)
-                == PackageManager.PERMISSION_GRANTED
-                && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+                == PackageManager.PERMISSION_GRANTED) {
             return null;
         }
         if (checkComponentPermission(cpi.writePermission, callingPid, callingUid,
@@ -4929,8 +5028,7 @@
                 PathPermission pp = pps[i];
                 if (checkComponentPermission(pp.getReadPermission(), callingPid, callingUid,
                         cpi.exported ? -1 : cpi.applicationInfo.uid)
-                        == PackageManager.PERMISSION_GRANTED
-                        && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+                        == PackageManager.PERMISSION_GRANTED) {
                     return null;
                 }
                 if (checkComponentPermission(pp.getWritePermission(), callingPid, callingUid,
@@ -4941,6 +5039,15 @@
             }
         }
         
+        HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+        if (perms != null) {
+            for (Map.Entry<Uri, UriPermission> uri : perms.entrySet()) {
+                if (uri.getKey().getAuthority().equals(cpi.authority)) {
+                    return null;
+                }
+            }
+        }
+
         String msg = "Permission Denial: opening provider " + cpi.name
                 + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
                 + ", uid=" + callingUid + ") requires "
@@ -4970,10 +5077,9 @@
             cpr = mProvidersByName.get(name);
             if (cpr != null) {
                 cpi = cpr.info;
-                if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
-                    return new ContentProviderHolder(cpi,
-                            cpi.readPermission != null
-                                    ? cpi.readPermission : cpi.writePermission);
+                String msg;
+                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+                    throw new SecurityException(msg);
                 }
 
                 if (r != null && cpr.canRunHere(r)) {
@@ -5033,10 +5139,9 @@
                     return null;
                 }
 
-                if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
-                    return new ContentProviderHolder(cpi,
-                            cpi.readPermission != null
-                                    ? cpi.readPermission : cpi.writePermission);
+                String msg;
+                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+                    throw new SecurityException(msg);
                 }
 
                 if (!mSystemReady && !mDidUpdate && !mWaitingUpdate
@@ -6208,7 +6313,7 @@
                 Binder.restoreCallingIdentity(origId);
             }
             int res = result.get();
-            Log.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
+            Slog.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
         }
     }
 
@@ -7347,12 +7452,14 @@
             if (mServiceConnections.size() > 0) {
                 if (needSep) pw.println(" ");
                 pw.println("  Connection bindings to services:");
-                Iterator<ConnectionRecord> it
+                Iterator<ArrayList<ConnectionRecord>> it
                         = mServiceConnections.values().iterator();
                 while (it.hasNext()) {
-                    ConnectionRecord r = it.next();
-                    pw.print("  * "); pw.println(r);
-                    r.dump(pw, "    ");
+                    ArrayList<ConnectionRecord> r = it.next();
+                    for (int i=0; i<r.size(); i++) {
+                        pw.print("  * "); pw.println(r.get(i));
+                        r.get(i).dump(pw, "    ");
+                    }
                 }
                 needSep = true;
             }
@@ -7630,18 +7737,21 @@
                 while (it.hasNext()) {
                     ServiceRecord r = it.next();
                     if (r.connections.size() > 0) {
-                        Iterator<ConnectionRecord> jt
+                        Iterator<ArrayList<ConnectionRecord>> jt
                                 = r.connections.values().iterator();
                         while (jt.hasNext()) {
-                            ConnectionRecord c = jt.next();
-                            if (c.binding.client != app) {
-                                try {
-                                    //c.conn.connected(r.className, null);
-                                } catch (Exception e) {
-                                    // todo: this should be asynchronous!
-                                    Slog.w(TAG, "Exception thrown disconnected servce "
-                                          + r.shortName
-                                          + " from app " + app.processName, e);
+                            ArrayList<ConnectionRecord> cl = jt.next();
+                            for (int i=0; i<cl.size(); i++) {
+                                ConnectionRecord c = cl.get(i);
+                                if (c.binding.client != app) {
+                                    try {
+                                        //c.conn.connected(r.className, null);
+                                    } catch (Exception e) {
+                                        // todo: this should be asynchronous!
+                                        Slog.w(TAG, "Exception thrown disconnected servce "
+                                              + r.shortName
+                                              + " from app " + app.processName, e);
+                                    }
                                 }
                             }
                         }
@@ -7671,7 +7781,9 @@
                 }
                 sr.app = null;
                 sr.executeNesting = 0;
-                mStoppingServices.remove(sr);
+                if (mStoppingServices.remove(sr)) {
+                    if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
+                }
                 
                 boolean hasClients = sr.bindings.size() > 0;
                 if (hasClients) {
@@ -7724,6 +7836,7 @@
             ServiceRecord sr = mStoppingServices.get(i);
             if (sr.app == app) {
                 mStoppingServices.remove(i);
+                if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
             }
         }
         
@@ -7987,11 +8100,15 @@
         if (r.app != null && r.app.persistent) {
             info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
         }
-        for (ConnectionRecord conn : r.connections.values()) {
-            if (conn.clientLabel != 0) {
-                info.clientPackage = conn.binding.client.info.packageName;
-                info.clientLabel = conn.clientLabel;
-                break;
+
+        for (ArrayList<ConnectionRecord> connl : r.connections.values()) {
+            for (int i=0; i<connl.size(); i++) {
+                ConnectionRecord conn = connl.get(i);
+                if (conn.clientLabel != 0) {
+                    info.clientPackage = conn.binding.client.info.packageName;
+                    info.clientLabel = conn.clientLabel;
+                    return info;
+                }
             }
         }
         return info;
@@ -8026,9 +8143,11 @@
         synchronized (this) {
             ServiceRecord r = mServices.get(name);
             if (r != null) {
-                for (ConnectionRecord conn : r.connections.values()) {
-                    if (conn.clientIntent != null) {
-                        return conn.clientIntent;
+                for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
+                    for (int i=0; i<conn.size(); i++) {
+                        if (conn.get(i).clientIntent != null) {
+                            return conn.get(i).clientIntent;
+                        }
                     }
                 }
             }
@@ -8202,18 +8321,24 @@
             return;
         }
 
-        int i = 0;
-        while (i < N) {
+        while (r.pendingStarts.size() > 0) {
             try {
-                ServiceRecord.StartItem si = r.pendingStarts.get(i);
-                if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to service: "
-                        + r.name + " " + r.intent + " args=" + si.intent);
-                if (si.intent == null && N > 1) {
+                ServiceRecord.StartItem si = r.pendingStarts.remove(0);
+                if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: "
+                        + r + " " + r.intent + " args=" + si.intent);
+                if (si.intent == null) {
                     // If somehow we got a dummy start at the front, then
                     // just drop it here.
-                    i++;
                     continue;
                 }
+                si.deliveredTime = SystemClock.uptimeMillis();
+                r.deliveredStarts.add(si);
+                si.deliveryCount++;
+                if (si.targetPermissionUid >= 0) {
+                    grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid,
+                            r.packageName, si.intent, si.getUriPermissionsLocked());
+                }
+                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING start of " + r);
                 bumpServiceExecutingLocked(r);
                 if (!oomAdjusted) {
                     oomAdjusted = true;
@@ -8227,27 +8352,16 @@
                     flags |= Service.START_FLAG_REDELIVERY;
                 }
                 r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
-                si.deliveredTime = SystemClock.uptimeMillis();
-                r.deliveredStarts.add(si);
-                si.deliveryCount++;
-                i++;
             } catch (RemoteException e) {
                 // Remote process gone...  we'll let the normal cleanup take
                 // care of this.
+                if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while scheduling start: " + r);
                 break;
             } catch (Exception e) {
                 Slog.w(TAG, "Unexpected exception", e);
                 break;
             }
         }
-        if (i == N) {
-            r.pendingStarts.clear();
-        } else {
-            while (i > 0) {
-                i--;
-                r.pendingStarts.remove(i);
-            }
-        }
     }
 
     private final boolean requestServiceBindingLocked(ServiceRecord r,
@@ -8258,9 +8372,9 @@
         }
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
+                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bind of " + r
+                        + " in " + i + ": shouldUnbind=" + i.hasBound);
                 bumpServiceExecutingLocked(r);
-                if (DEBUG_SERVICE) Slog.v(TAG, "Connecting binding " + i
-                        + ": shouldUnbind=" + i.hasBound);
                 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
                 if (!rebind) {
                     i.requested = true;
@@ -8268,6 +8382,7 @@
                 i.hasBound = true;
                 i.doRebind = false;
             } catch (RemoteException e) {
+                if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
                 return false;
             }
         }
@@ -8294,13 +8409,12 @@
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
         app.services.add(r);
+        if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING create of " + r + " " + r.intent);
         bumpServiceExecutingLocked(r);
         updateLruProcessLocked(app, true, true);
 
         boolean created = false;
         try {
-            if (DEBUG_SERVICE) Slog.v(TAG, "Scheduling start service: "
-                    + r.name + " " + r.intent);
             mStringBuilder.setLength(0);
             r.intent.getIntent().toShortString(mStringBuilder, false, true);
             EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE,
@@ -8330,7 +8444,7 @@
             if (r.lastStartId < 1) {
                 r.lastStartId = 1;
             }
-            r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, null));
+            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
         }
         
         sendServiceArgsLocked(r, true);
@@ -8350,6 +8464,7 @@
         if (N > 0) {
             for (int i=N-1; i>=0; i--) {
                 ServiceRecord.StartItem si = r.deliveredStarts.get(i);
+                si.removeUriPermissionsLocked();
                 if (si.intent == null) {
                     // We'll generate this again if needed.
                 } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
@@ -8459,8 +8574,7 @@
             return true;
         }
 
-        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up service " + r.name
-                + " " + r.intent);
+        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
 
         // We are now bringing the service up, so no longer in the
         // restarting state.
@@ -8511,27 +8625,30 @@
             if (!force) {
                 // XXX should probably keep a count of the number of auto-create
                 // connections directly in the service.
-                Iterator<ConnectionRecord> it = r.connections.values().iterator();
+                Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
                 while (it.hasNext()) {
-                    ConnectionRecord cr = it.next();
-                    if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
-                        return;
+                    ArrayList<ConnectionRecord> cr = it.next();
+                    for (int i=0; i<cr.size(); i++) {
+                        if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
+                            return;
+                        }
                     }
                 }
             }
 
             // Report to all of the connections that the service is no longer
             // available.
-            Iterator<ConnectionRecord> it = r.connections.values().iterator();
+            Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
             while (it.hasNext()) {
-                ConnectionRecord c = it.next();
-                try {
-                    // todo: shouldn't be a synchronous call!
-                    c.conn.connected(r.name, null);
-                } catch (Exception e) {
-                    Slog.w(TAG, "Failure disconnecting service " + r.name +
-                          " to connection " + c.conn.asBinder() +
-                          " (in " + c.binding.client.processName + ")", e);
+                ArrayList<ConnectionRecord> c = it.next();
+                for (int i=0; i<c.size(); i++) {
+                    try {
+                        c.get(i).conn.connected(r.name, null);
+                    } catch (Exception e) {
+                        Slog.w(TAG, "Failure disconnecting service " + r.name +
+                              " to connection " + c.get(i).conn.asBinder() +
+                              " (in " + c.get(i).binding.client.processName + ")", e);
+                    }
                 }
             }
         }
@@ -8545,6 +8662,8 @@
                         + ": hasBound=" + ibr.hasBound);
                 if (r.app != null && r.app.thread != null && ibr.hasBound) {
                     try {
+                        if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bring down unbind of " + r
+                                + " for " + ibr);
                         bumpServiceExecutingLocked(r);
                         updateOomAdjLocked(r.app);
                         ibr.hasBound = false;
@@ -8559,15 +8678,13 @@
             }
         }
 
-        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down service " + r.name
-                 + " " + r.intent);
+        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent);
         EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE,
                 System.identityHashCode(r), r.shortName,
                 (r.app != null) ? r.app.pid : -1);
 
         mServices.remove(r.name);
         mServicesByIntent.remove(r.intent);
-        if (localLOGV) Slog.v(TAG, "BRING DOWN SERVICE: " + r.shortName);
         r.totalRestartCount = 0;
         unscheduleServiceRestartLocked(r);
 
@@ -8576,8 +8693,7 @@
         for (int i=0; i<N; i++) {
             if (mPendingServices.get(i) == r) {
                 mPendingServices.remove(i);
-                if (DEBUG_SERVICE) Slog.v(
-                    TAG, "Removed pending service: " + r.shortName);
+                if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r);
                 i--;
                 N--;
             }
@@ -8589,7 +8705,7 @@
         r.foregroundNoti = null;
         
         // Clear start entries.
-        r.deliveredStarts.clear();
+        r.clearDeliveredStartsLocked();
         r.pendingStarts.clear();
         
         if (r.app != null) {
@@ -8599,8 +8715,11 @@
             r.app.services.remove(r);
             if (r.app.thread != null) {
                 try {
-                    if (DEBUG_SERVICE) Slog.v(TAG,
-                            "Stopping service: " + r.shortName);
+                    if (DEBUG_SERVICE) {
+                        RuntimeException here = new RuntimeException();
+                        here.fillInStackTrace();
+                        Slog.v(TAG, ">>> EXECUTING stop of " + r, here);
+                    }
                     bumpServiceExecutingLocked(r);
                     mStoppingServices.add(r);
                     updateOomAdjLocked(r.app);
@@ -8613,11 +8732,11 @@
                 updateServiceForegroundLocked(r.app, false);
             } else {
                 if (DEBUG_SERVICE) Slog.v(
-                    TAG, "Removed service that has no process: " + r.shortName);
+                    TAG, "Removed service that has no process: " + r);
             }
         } else {
             if (DEBUG_SERVICE) Slog.v(
-                TAG, "Removed service that is not running: " + r.shortName);
+                TAG, "Removed service that is not running: " + r);
         }
     }
 
@@ -8649,9 +8768,10 @@
                         ? res.permission : "private to package");
             }
             ServiceRecord r = res.record;
+            int targetPermissionUid = checkGrantUriPermissionFromIntentLocked(
+                    callingUid, r.packageName, service);
             if (unscheduleServiceRestartLocked(r)) {
-                if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: "
-                        + r.shortName);
+                if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
             }
             r.startRequested = true;
             r.callStart = false;
@@ -8659,7 +8779,8 @@
             if (r.lastStartId < 1) {
                 r.lastStartId = 1;
             }
-            r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, service));
+            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId,
+                    service, targetPermissionUid));
             r.lastActivity = SystemClock.uptimeMillis();
             synchronized (r.stats.getBatteryStats()) {
                 r.stats.startRunningLocked();
@@ -8784,7 +8905,9 @@
                     ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
                     if (si != null) {
                         while (r.deliveredStarts.size() > 0) {
-                            if (r.deliveredStarts.remove(0) == si) {
+                            ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
+                            cur.removeUriPermissionsLocked();
+                            if (cur == si) {
                                 break;
                             }
                         }
@@ -8942,7 +9065,7 @@
 
             if (unscheduleServiceRestartLocked(s)) {
                 if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: "
-                        + s.shortName);
+                        + s);
             }
 
             AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
@@ -8950,7 +9073,12 @@
                     connection, flags, clientLabel, clientIntent);
 
             IBinder binder = connection.asBinder();
-            s.connections.put(binder, c);
+            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+            if (clist == null) {
+                clist = new ArrayList<ConnectionRecord>();
+                s.connections.put(binder, clist);
+            }
+            clist.add(c);
             b.connections.add(c);
             if (activity != null) {
                 if (activity.connections == null) {
@@ -8959,7 +9087,12 @@
                 activity.connections.add(c);
             }
             b.client.connections.add(c);
-            mServiceConnections.put(binder, c);
+            clist = mServiceConnections.get(binder);
+            if (clist == null) {
+                clist = new ArrayList<ConnectionRecord>();
+                mServiceConnections.put(binder, clist);
+            }
+            clist.add(c);
 
             if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                 s.lastActivity = SystemClock.uptimeMillis();
@@ -9010,7 +9143,13 @@
         IBinder binder = c.conn.asBinder();
         AppBindRecord b = c.binding;
         ServiceRecord s = b.service;
-        s.connections.remove(binder);
+        ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+        if (clist != null) {
+            clist.remove(c);
+            if (clist.size() == 0) {
+                s.connections.remove(binder);
+            }
+        }
         b.connections.remove(c);
         if (c.activity != null && c.activity != skipAct) {
             if (c.activity.connections != null) {
@@ -9020,7 +9159,13 @@
         if (b.client != skipApp) {
             b.client.connections.remove(c);
         }
-        mServiceConnections.remove(binder);
+        clist = mServiceConnections.get(binder);
+        if (clist != null) {
+            clist.remove(c);
+            if (clist.size() == 0) {
+                mServiceConnections.remove(binder);
+            }
+        }
 
         if (b.connections.size() == 0) {
             b.intent.apps.remove(b.client);
@@ -9031,6 +9176,8 @@
         if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
                 && b.intent.hasBound) {
             try {
+                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING unbind of " + s
+                        + " from " + b);
                 bumpServiceExecutingLocked(s);
                 updateOomAdjLocked(s.app);
                 b.intent.hasBound = false;
@@ -9053,8 +9200,8 @@
         synchronized (this) {
             IBinder binder = connection.asBinder();
             if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder);
-            ConnectionRecord r = mServiceConnections.get(binder);
-            if (r == null) {
+            ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+            if (clist == null) {
                 Slog.w(TAG, "Unbind failed: could not find connection for "
                       + connection.asBinder());
                 return false;
@@ -9062,11 +9209,14 @@
 
             final long origId = Binder.clearCallingIdentity();
 
-            removeConnectionLocked(r, null, null);
+            while (clist.size() > 0) {
+                ConnectionRecord r = clist.get(0);
+                removeConnectionLocked(r, null, null);
 
-            if (r.binding.service.app != null) {
-                // This could have made the service less important.
-                updateOomAdjLocked(r.binding.service.app);
+                if (r.binding.service.app != null) {
+                    // This could have made the service less important.
+                    updateOomAdjLocked(r.binding.service.app);
+                }
             }
 
             Binder.restoreCallingIdentity(origId);
@@ -9089,7 +9239,7 @@
 
             final long origId = Binder.clearCallingIdentity();
 
-            if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING SERVICE " + r.name
+            if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
                     + " " + intent + ": " + service);
             if (r != null) {
                 Intent.FilterComparison filter
@@ -9100,26 +9250,29 @@
                     b.requested = true;
                     b.received = true;
                     if (r.connections.size() > 0) {
-                        Iterator<ConnectionRecord> it
+                        Iterator<ArrayList<ConnectionRecord>> it
                                 = r.connections.values().iterator();
                         while (it.hasNext()) {
-                            ConnectionRecord c = it.next();
-                            if (!filter.equals(c.binding.intent.intent)) {
-                                if (DEBUG_SERVICE) Slog.v(
-                                        TAG, "Not publishing to: " + c);
-                                if (DEBUG_SERVICE) Slog.v(
-                                        TAG, "Bound intent: " + c.binding.intent.intent);
-                                if (DEBUG_SERVICE) Slog.v(
-                                        TAG, "Published intent: " + intent);
-                                continue;
-                            }
-                            if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
-                            try {
-                                c.conn.connected(r.name, service);
-                            } catch (Exception e) {
-                                Slog.w(TAG, "Failure sending service " + r.name +
-                                      " to connection " + c.conn.asBinder() +
-                                      " (in " + c.binding.client.processName + ")", e);
+                            ArrayList<ConnectionRecord> clist = it.next();
+                            for (int i=0; i<clist.size(); i++) {
+                                ConnectionRecord c = clist.get(i);
+                                if (!filter.equals(c.binding.intent.intent)) {
+                                    if (DEBUG_SERVICE) Slog.v(
+                                            TAG, "Not publishing to: " + c);
+                                    if (DEBUG_SERVICE) Slog.v(
+                                            TAG, "Bound intent: " + c.binding.intent.intent);
+                                    if (DEBUG_SERVICE) Slog.v(
+                                            TAG, "Published intent: " + intent);
+                                    continue;
+                                }
+                                if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
+                                try {
+                                    c.conn.connected(r.name, service);
+                                } catch (Exception e) {
+                                    Slog.w(TAG, "Failure sending service " + r.name +
+                                          " to connection " + c.conn.asBinder() +
+                                          " (in " + c.binding.client.processName + ")", e);
+                                }
                             }
                         }
                     }
@@ -9180,9 +9333,6 @@
             ServiceRecord r = (ServiceRecord)token;
             boolean inStopping = mStoppingServices.contains(token);
             if (r != null) {
-                if (DEBUG_SERVICE) Slog.v(TAG, "DONE EXECUTING SERVICE " + r.name
-                        + ": nesting=" + r.executeNesting
-                        + ", inStopping=" + inStopping);
                 if (r != token) {
                     Slog.w(TAG, "Done executing service " + r.name
                           + " with incorrect token: given " + token
@@ -9239,13 +9389,16 @@
                 serviceDoneExecutingLocked(r, inStopping);
                 Binder.restoreCallingIdentity(origId);
             } else {
-                Slog.w(TAG, "Done executing unknown service " + r.name
-                        + " with token " + token);
+                Slog.w(TAG, "Done executing unknown service from pid "
+                        + Binder.getCallingPid());
             }
         }
     }
 
     public void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) {
+        if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
+                + ": nesting=" + r.executeNesting
+                + ", inStopping=" + inStopping + ", app=" + r.app);
         r.executeNesting--;
         if (r.executeNesting <= 0 && r.app != null) {
             r.app.executingServices.remove(r);
@@ -9253,6 +9406,7 @@
                 mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app);
             }
             if (inStopping) {
+                if (DEBUG_SERVICE) Slog.v(TAG, "doneExecuting remove stopping " + r);
                 mStoppingServices.remove(r);
             }
             updateOomAdjLocked(r.app);
@@ -11043,61 +11197,64 @@
                 }
                 if (s.connections.size() > 0 && (adj > FOREGROUND_APP_ADJ
                         || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
-                    Iterator<ConnectionRecord> kt
+                    Iterator<ArrayList<ConnectionRecord>> kt
                             = s.connections.values().iterator();
                     while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) {
-                        // XXX should compute this based on the max of
-                        // all connected clients.
-                        ConnectionRecord cr = kt.next();
-                        if (cr.binding.client == app) {
-                            // Binding to ourself is not interesting.
-                            continue;
-                        }
-                        if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
-                            ProcessRecord client = cr.binding.client;
-                            int myHiddenAdj = hiddenAdj;
-                            if (myHiddenAdj > client.hiddenAdj) {
-                                if (client.hiddenAdj >= VISIBLE_APP_ADJ) {
-                                    myHiddenAdj = client.hiddenAdj;
-                                } else {
-                                    myHiddenAdj = VISIBLE_APP_ADJ;
+                        ArrayList<ConnectionRecord> clist = kt.next();
+                        for (int i=0; i<clist.size() && adj > FOREGROUND_APP_ADJ; i++) {
+                            // XXX should compute this based on the max of
+                            // all connected clients.
+                            ConnectionRecord cr = clist.get(i);
+                            if (cr.binding.client == app) {
+                                // Binding to ourself is not interesting.
+                                continue;
+                            }
+                            if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
+                                ProcessRecord client = cr.binding.client;
+                                int myHiddenAdj = hiddenAdj;
+                                if (myHiddenAdj > client.hiddenAdj) {
+                                    if (client.hiddenAdj >= VISIBLE_APP_ADJ) {
+                                        myHiddenAdj = client.hiddenAdj;
+                                    } else {
+                                        myHiddenAdj = VISIBLE_APP_ADJ;
+                                    }
+                                }
+                                int clientAdj = computeOomAdjLocked(
+                                    client, myHiddenAdj, TOP_APP, true);
+                                if (adj > clientAdj) {
+                                    adj = clientAdj >= VISIBLE_APP_ADJ
+                                            ? clientAdj : VISIBLE_APP_ADJ;
+                                    if (!client.hidden) {
+                                        app.hidden = false;
+                                    }
+                                    app.adjType = "service";
+                                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                            .REASON_SERVICE_IN_USE;
+                                    app.adjSource = cr.binding.client;
+                                    app.adjTarget = s.name;
+                                }
+                                if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                                    if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+                                        schedGroup = Process.THREAD_GROUP_DEFAULT;
+                                    }
                                 }
                             }
-                            int clientAdj = computeOomAdjLocked(
-                                client, myHiddenAdj, TOP_APP, true);
-                            if (adj > clientAdj) {
-                                adj = clientAdj >= VISIBLE_APP_ADJ
-                                        ? clientAdj : VISIBLE_APP_ADJ;
-                                if (!client.hidden) {
-                                    app.hidden = false;
-                                }
+                            ActivityRecord a = cr.activity;
+                            //if (a != null) {
+                            //    Slog.i(TAG, "Connection to " + a ": state=" + a.state);
+                            //}
+                            if (a != null && adj > FOREGROUND_APP_ADJ &&
+                                    (a.state == ActivityState.RESUMED
+                                     || a.state == ActivityState.PAUSING)) {
+                                adj = FOREGROUND_APP_ADJ;
+                                schedGroup = Process.THREAD_GROUP_DEFAULT;
+                                app.hidden = false;
                                 app.adjType = "service";
                                 app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                         .REASON_SERVICE_IN_USE;
-                                app.adjSource = cr.binding.client;
+                                app.adjSource = a;
                                 app.adjTarget = s.name;
                             }
-                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
-                                if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
-                                    schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                }
-                            }
-                        }
-                        ActivityRecord a = cr.activity;
-                        //if (a != null) {
-                        //    Slog.i(TAG, "Connection to " + a ": state=" + a.state);
-                        //}
-                        if (a != null && adj > FOREGROUND_APP_ADJ &&
-                                (a.state == ActivityState.RESUMED
-                                 || a.state == ActivityState.PAUSING)) {
-                            adj = FOREGROUND_APP_ADJ;
-                            schedGroup = Process.THREAD_GROUP_DEFAULT;
-                            app.hidden = false;
-                            app.adjType = "service";
-                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                    .REASON_SERVICE_IN_USE;
-                            app.adjSource = a;
-                            app.adjTarget = s.name;
                         }
                     }
                 }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 79756a7..62be918 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -29,6 +29,7 @@
 import android.os.Bundle;
 import android.os.Message;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.EventLog;
 import android.util.Log;
@@ -77,8 +78,7 @@
     HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
     ArrayList newIntents;   // any pending new intents for single-top mode
     HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
-    HashSet<UriPermission> readUriPermissions; // special access to reading uris.
-    HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
+    UriPermissionOwner uriPermissions; // current special URI access perms.
     ProcessRecord app;  // if non-null, hosting application
     Bitmap thumbnail;       // icon representation of paused screen
     CharSequence description; // textual description of paused screen
@@ -140,11 +140,15 @@
         if (pendingResults != null) {
             pw.print(prefix); pw.print("pendingResults="); pw.println(pendingResults);
         }
-        if (readUriPermissions != null) {
-            pw.print(prefix); pw.print("readUriPermissions="); pw.println(readUriPermissions);
-        }
-        if (writeUriPermissions != null) {
-            pw.print(prefix); pw.print("writeUriPermissions="); pw.println(writeUriPermissions);
+        if (uriPermissions != null) {
+            if (uriPermissions.readUriPermissions != null) {
+                pw.print(prefix); pw.print("readUriPermissions=");
+                        pw.println(uriPermissions.readUriPermissions);
+            }
+            if (uriPermissions.writeUriPermissions != null) {
+                pw.print(prefix); pw.print("writeUriPermissions=");
+                        pw.println(uriPermissions.writeUriPermissions);
+            }
         }
         pw.print(prefix); pw.print("launchFailed="); pw.print(launchFailed);
                 pw.print(" haveState="); pw.print(haveState);
@@ -300,6 +304,13 @@
         }
     }
 
+    UriPermissionOwner getUriPermissionsLocked() {
+        if (uriPermissions == null) {
+            uriPermissions = new UriPermissionOwner(service, this);
+        }
+        return uriPermissions;
+    }
+
     void addResultLocked(ActivityRecord from, String resultWho,
             int requestCode, int resultCode,
             Intent resultData) {
@@ -340,16 +351,22 @@
      * Deliver a new Intent to an existing activity, so that its onNewIntent()
      * method will be called at the proper time.
      */
-    final void deliverNewIntentLocked(Intent intent) {
+    final void deliverNewIntentLocked(int callingUid, Intent intent) {
         boolean sent = false;
         if (state == ActivityState.RESUMED
                 && app != null && app.thread != null) {
             try {
                 ArrayList<Intent> ar = new ArrayList<Intent>();
-                ar.add(new Intent(intent));
+                intent = new Intent(intent);
+                ar.add(intent);
+                service.grantUriPermissionFromIntentLocked(callingUid, packageName,
+                        intent, getUriPermissionsLocked());
                 app.thread.scheduleNewIntent(ar, this);
                 sent = true;
-            } catch (Exception e) {
+            } catch (RemoteException e) {
+                Slog.w(ActivityManagerService.TAG,
+                        "Exception thrown sending new intent to " + this, e);
+            } catch (NullPointerException e) {
                 Slog.w(ActivityManagerService.TAG,
                         "Exception thrown sending new intent to " + this, e);
             }
@@ -360,25 +377,9 @@
     }
 
     void removeUriPermissionsLocked() {
-        if (readUriPermissions != null) {
-            for (UriPermission perm : readUriPermissions) {
-                perm.readActivities.remove(this);
-                if (perm.readActivities.size() == 0 && (perm.globalModeFlags
-                        &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
-                    perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-                   service.removeUriPermissionIfNeededLocked(perm);
-                }
-            }
-        }
-        if (writeUriPermissions != null) {
-            for (UriPermission perm : writeUriPermissions) {
-                perm.writeActivities.remove(this);
-                if (perm.writeActivities.size() == 0 && (perm.globalModeFlags
-                        &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
-                    perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-                    service.removeUriPermissionIfNeededLocked(perm);
-                }
-            }
+        if (uriPermissions != null) {
+            uriPermissions.removeUriPermissionsLocked();
+            uriPermissions = null;
         }
     }
 
@@ -569,7 +570,6 @@
                 state == ActivityState.RESUMED;
      }
     
-    
     public String toString() {
         if (stringName != null) {
             return stringName;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index de7b15c..c21a221 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -504,7 +504,7 @@
                     r.info, r.icicle, results, newIntents, !andResume,
                     mService.isNextTransitionForward());
             
-            if ((app.info.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+            if ((app.info.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
                 // This may be a heavy-weight process!  Note that the package
                 // manager will ensure that only activity can run in the main
                 // process of the .apk, which is the only thing that will be
@@ -2033,16 +2033,6 @@
             }
         }
 
-        if (grantedUriPermissions != null && callingUid > 0) {
-            for (int i=0; i<grantedUriPermissions.length; i++) {
-                mService.grantUriPermissionLocked(callingUid, r.packageName,
-                        grantedUriPermissions[i], grantedMode, r);
-            }
-        }
-
-        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                intent, r);
-
         if (sourceRecord == null) {
             // This activity is not being started from another...  in this
             // case we -always- start a new task.
@@ -2150,7 +2140,7 @@
                                 top.task.setIntent(r.intent, r.info);
                             }
                             logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                            top.deliverNewIntentLocked(r.intent);
+                            top.deliverNewIntentLocked(callingUid, r.intent);
                         } else {
                             // A special case: we need to
                             // start the activity because it is not currently
@@ -2175,7 +2165,7 @@
                             if (taskTop.frontOfTask) {
                                 taskTop.task.setIntent(r.intent, r.info);
                             }
-                            taskTop.deliverNewIntentLocked(r.intent);
+                            taskTop.deliverNewIntentLocked(callingUid, r.intent);
                         } else if (!r.intent.filterEquals(taskTop.task.intent)) {
                             // In this case we are launching the root activity
                             // of the task, but with a different intent.  We
@@ -2243,7 +2233,7 @@
                                 // is the case, so this is it!
                                 return START_RETURN_INTENT_TO_CALLER;
                             }
-                            top.deliverNewIntentLocked(r.intent);
+                            top.deliverNewIntentLocked(callingUid, r.intent);
                             return START_DELIVERED_TO_TOP;
                         }
                     }
@@ -2288,7 +2278,7 @@
                         sourceRecord.task.taskId, r, launchFlags, true);
                 if (top != null) {
                     logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    top.deliverNewIntentLocked(r.intent);
+                    top.deliverNewIntentLocked(callingUid, r.intent);
                     // For paranoia, make sure we have correctly
                     // resumed the top activity.
                     if (doResume) {
@@ -2305,7 +2295,7 @@
                 if (where >= 0) {
                     ActivityRecord top = moveActivityToFrontLocked(where);
                     logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    top.deliverNewIntentLocked(r.intent);
+                    top.deliverNewIntentLocked(callingUid, r.intent);
                     if (doResume) {
                         resumeTopActivityLocked(null);
                     }
@@ -2333,6 +2323,17 @@
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                     + " in new guessed " + r.task);
         }
+
+        if (grantedUriPermissions != null && callingUid > 0) {
+            for (int i=0; i<grantedUriPermissions.length; i++) {
+                mService.grantUriPermissionLocked(callingUid, r.packageName,
+                        grantedUriPermissions[i], grantedMode, r.getUriPermissionsLocked());
+            }
+        }
+
+        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+                intent, r.getUriPermissionsLocked());
+
         if (newTask) {
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
         }
@@ -2403,7 +2404,7 @@
             final long origId = Binder.clearCallingIdentity();
             
             if (mMainStack && aInfo != null &&
-                    (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+                    (aInfo.applicationInfo.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
                 // This may be a heavy-weight process!  Check to see if we already
                 // have another, different heavy-weight process running.
                 if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
@@ -2556,7 +2557,7 @@
 
         if (callingUid > 0) {
             mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                    data, r);
+                    data, r.getUriPermissionsLocked());
         }
 
         if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
@@ -2884,7 +2885,7 @@
                     + " res=" + resultCode + " data=" + resultData);
             if (r.info.applicationInfo.uid > 0) {
                 mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
-                        r.packageName, resultData, r);
+                        r.packageName, resultData, r.getUriPermissionsLocked());
             }
             resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
                                      resultData);
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index c3f0b3e..bac21b1 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -26,6 +26,7 @@
 import android.os.IBinder;
 import android.os.SystemClock;
 import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -73,61 +74,65 @@
     ActivityInfo curReceiver;   // info about the receiver that is currently running.
 
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
-        pw.println(prefix + intent);
+        final long now = SystemClock.uptimeMillis();
+
+        pw.print(prefix); pw.println(this);
+        pw.print(prefix); pw.println(intent);
         if (sticky) {
             Bundle bundle = intent.getExtras();
             if (bundle != null) {
-                pw.println(prefix + "extras: " + bundle.toString());
+                pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
             }
         }
-        pw.println(prefix + "proc=" + callerApp);
-        pw.println(prefix + "caller=" + callerPackage
-                + " callingPid=" + callingPid
-                + " callingUid=" + callingUid);
+        pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.println(" ");
+                pw.println(callerApp != null ? callerApp.toShortString() : "null");
+                pw.print(" pid="); pw.print(callingPid);
+                pw.print(" uid="); pw.println(callingUid);
         if (requiredPermission != null) {
-            pw.println(prefix + "requiredPermission=" + requiredPermission);
+            pw.print(prefix); pw.print("requiredPermission="); pw.println(requiredPermission);
         }
-        pw.println(prefix + "dispatchTime=" + dispatchTime + " ("
-                + (SystemClock.uptimeMillis()-dispatchTime) + "ms since now)");
+        pw.print(prefix); pw.print("dispatchTime=");
+                TimeUtils.formatDuration(dispatchTime, now, pw);
         if (finishTime != 0) {
-            pw.println(prefix + "finishTime=" + finishTime + " ("
-                    + (SystemClock.uptimeMillis()-finishTime) + "ms since now)");
+            pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw);
         } else {
-            pw.println(prefix + "receiverTime=" + receiverTime + " ("
-                    + (SystemClock.uptimeMillis()-receiverTime) + "ms since now)");
+            pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw);
         }
+        pw.println("");
         if (anrCount != 0) {
-            pw.println(prefix + "anrCount=" + anrCount);
+            pw.print(prefix); pw.print("anrCount="); pw.println(anrCount);
         }
         if (resultTo != null || resultCode != -1 || resultData != null) {
-            pw.println(prefix + "resultTo=" + resultTo
-                  + " resultCode=" + resultCode + " resultData=" + resultData);
+            pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
+                    pw.print(" resultCode="); pw.print(resultCode);
+                    pw.print(" resultData="); pw.println(resultData);
         }
         if (resultExtras != null) {
-            pw.println(prefix + "resultExtras=" + resultExtras);
+            pw.print(prefix); pw.print("resultExtras="); pw.println(resultExtras);
         }
         if (resultAbort || ordered || sticky || initialSticky) {
-            pw.println(prefix + "resultAbort=" + resultAbort
-                    + " ordered=" + ordered + " sticky=" + sticky
-                    + " initialSticky=" + initialSticky);
+            pw.print(prefix); pw.print("resultAbort="); pw.print(resultAbort);
+                    pw.print(" ordered="); pw.print(ordered);
+                    pw.print(" sticky="); pw.print(sticky);
+                    pw.print(" initialSticky="); pw.println(initialSticky);
         }
         if (nextReceiver != 0 || receiver != null) {
-            pw.println(prefix + "nextReceiver=" + nextReceiver
-                  + " receiver=" + receiver);
+            pw.print(prefix); pw.print("nextReceiver="); pw.print(nextReceiver);
+                    pw.print(" receiver="); pw.println(receiver);
         }
         if (curFilter != null) {
-            pw.println(prefix + "curFilter=" + curFilter);
+            pw.print(prefix); pw.print("curFilter="); pw.println(curFilter);
         }
         if (curReceiver != null) {
-            pw.println(prefix + "curReceiver=" + curReceiver);
+            pw.print(prefix); pw.print("curReceiver="); pw.println(curReceiver);
         }
         if (curApp != null) {
-            pw.println(prefix + "curApp=" + curApp);
-            pw.println(prefix + "curComponent="
-                    + (curComponent != null ? curComponent.toShortString() : "--"));
+            pw.print(prefix); pw.print("curApp="); pw.println(curApp);
+            pw.print(prefix); pw.print("curComponent=");
+                    pw.println((curComponent != null ? curComponent.toShortString() : "--"));
             if (curReceiver != null && curReceiver.applicationInfo != null) {
-                pw.println(prefix + "curSourceDir=" + curReceiver.applicationInfo.sourceDir);
+                pw.print(prefix); pw.print("curSourceDir=");
+                        pw.println(curReceiver.applicationInfo.sourceDir);
             }
         }
         String stateStr = " (?)";
@@ -137,13 +142,14 @@
             case CALL_IN_RECEIVE:   stateStr=" (CALL_IN_RECEIVE)"; break;
             case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break;
         }
-        pw.println(prefix + "state=" + state + stateStr);
+        pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
         final int N = receivers != null ? receivers.size() : 0;
         String p2 = prefix + "  ";
         PrintWriterPrinter printer = new PrintWriterPrinter(pw);
         for (int i=0; i<N; i++) {
             Object o = receivers.get(i);
-            pw.println(prefix + "Receiver #" + i + ": " + o);
+            pw.print(prefix); pw.print("Receiver #"); pw.print(i);
+                    pw.print(": "); pw.println(o);
             if (o instanceof BroadcastFilter)
                 ((BroadcastFilter)o).dumpBrief(pw, p2);
             else if (o instanceof ResolveInfo)
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index ab5a78d..f35a68e 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -36,6 +36,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 
@@ -43,6 +44,12 @@
  * A running application service.
  */
 class ServiceRecord extends Binder {
+    // Maximum number of delivery attempts before giving up.
+    static final int MAX_DELIVERY_COUNT = 3;
+
+    // Maximum number of times it can fail during execution before giving up.
+    static final int MAX_DONE_EXECUTING_COUNT = 6;
+
     final ActivityManagerService ams;
     final BatteryStatsImpl.Uid.Pkg.Serv stats;
     final ComponentName name; // service component.
@@ -65,32 +72,9 @@
     final HashMap<Intent.FilterComparison, IntentBindRecord> bindings
             = new HashMap<Intent.FilterComparison, IntentBindRecord>();
                             // All active bindings to the service.
-    final HashMap<IBinder, ConnectionRecord> connections
-            = new HashMap<IBinder, ConnectionRecord>();
+    final HashMap<IBinder, ArrayList<ConnectionRecord>> connections
+            = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
                             // IBinder -> ConnectionRecord of all bound clients
-    
-    // Maximum number of delivery attempts before giving up.
-    static final int MAX_DELIVERY_COUNT = 3;
-    
-    // Maximum number of times it can fail during execution before giving up.
-    static final int MAX_DONE_EXECUTING_COUNT = 6;
-    
-    static class StartItem {
-        final int id;
-        final Intent intent;
-        long deliveredTime;
-        int deliveryCount;
-        int doneExecutingCount;
-        
-        StartItem(int _id, Intent _intent) {
-            id = _id;
-            intent = _intent;
-        }
-    }
-    final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
-                            // start() arguments which been delivered.
-    final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
-                            // start() arguments that haven't yet been delivered.
 
     ProcessRecord app;      // where this service is running or null.
     boolean isForeground;   // is service currently in foreground mode?
@@ -112,6 +96,59 @@
 
     String stringName;      // caching of toString
     
+    static class StartItem {
+        final ServiceRecord sr;
+        final int id;
+        final Intent intent;
+        final int targetPermissionUid;
+        long deliveredTime;
+        int deliveryCount;
+        int doneExecutingCount;
+        UriPermissionOwner uriPermissions;
+
+        String stringName;      // caching of toString
+
+        StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) {
+            sr = _sr;
+            id = _id;
+            intent = _intent;
+            targetPermissionUid = _targetPermissionUid;
+        }
+
+        UriPermissionOwner getUriPermissionsLocked() {
+            if (uriPermissions == null) {
+                uriPermissions = new UriPermissionOwner(sr.ams, this);
+            }
+            return uriPermissions;
+        }
+
+        void removeUriPermissionsLocked() {
+            if (uriPermissions != null) {
+                uriPermissions.removeUriPermissionsLocked();
+                uriPermissions = null;
+            }
+        }
+
+        public String toString() {
+            if (stringName != null) {
+                return stringName;
+            }
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("ServiceRecord{")
+                .append(Integer.toHexString(System.identityHashCode(sr)))
+                .append(' ').append(sr.shortName)
+                .append(" StartItem ")
+                .append(Integer.toHexString(System.identityHashCode(this)))
+                .append(" id=").append(id).append('}');
+            return stringName = sb.toString();
+        }
+    }
+
+    final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
+                            // start() arguments which been delivered.
+    final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
+                            // start() arguments that haven't yet been delivered.
+
     void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) {
         final int N = list.size();
         for (int i=0; i<N; i++) {
@@ -128,9 +165,24 @@
                     if (si.doneExecutingCount != 0) {
                         pw.print(" dxc="); pw.print(si.doneExecutingCount);
                     }
-                    pw.print(" ");
+                    pw.println("");
+            pw.print(prefix); pw.print("  intent=");
                     if (si.intent != null) pw.println(si.intent.toString());
                     else pw.println("null");
+            if (si.targetPermissionUid >= 0) {
+                pw.print(prefix); pw.print("  targetPermissionUid=");
+                        pw.println(si.targetPermissionUid);
+            }
+            if (si.uriPermissions != null) {
+                if (si.uriPermissions.readUriPermissions != null) {
+                    pw.print(prefix); pw.print("  readUriPermissions=");
+                            pw.println(si.uriPermissions.readUriPermissions);
+                }
+                if (si.uriPermissions.writeUriPermissions != null) {
+                    pw.print(prefix); pw.print("  writeUriPermissions=");
+                            pw.println(si.uriPermissions.writeUriPermissions);
+                }
+            }
         }
     }
     
@@ -201,10 +253,12 @@
         }
         if (connections.size() > 0) {
             pw.print(prefix); pw.println("All Connections:");
-            Iterator<ConnectionRecord> it = connections.values().iterator();
+            Iterator<ArrayList<ConnectionRecord>> it = connections.values().iterator();
             while (it.hasNext()) {
-                ConnectionRecord c = it.next();
-                pw.print(prefix); pw.print("  "); pw.println(c);
+                ArrayList<ConnectionRecord> c = it.next();
+                for (int i=0; i<c.size(); i++) {
+                    pw.print(prefix); pw.print("  "); pw.println(c.get(i));
+                }
             }
         }
     }
@@ -324,6 +378,13 @@
         }
     }
     
+    public void clearDeliveredStartsLocked() {
+        for (int i=deliveredStarts.size()-1; i>=0; i--) {
+            deliveredStarts.get(i).removeUriPermissionsLocked();
+        }
+        deliveredStarts.clear();
+    }
+
     public String toString() {
         if (stringName != null) {
             return stringName;
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index 81450c5..c95546e 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -22,13 +22,21 @@
 import java.io.PrintWriter;
 import java.util.HashSet;
 
+/**
+ * Description of a permission granted to an app to access a particular URI.
+ *
+ * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+ *
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert
+ *      /src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ */
 class UriPermission {
     final int uid;
     final Uri uri;
     int modeFlags = 0;
     int globalModeFlags = 0;
-    final HashSet<ActivityRecord> readActivities = new HashSet<ActivityRecord>();
-    final HashSet<ActivityRecord> writeActivities = new HashSet<ActivityRecord>();
+    final HashSet<UriPermissionOwner> readOwners = new HashSet<UriPermissionOwner>();
+    final HashSet<UriPermissionOwner> writeOwners = new HashSet<UriPermissionOwner>();
     
     String stringName;
     
@@ -41,27 +49,21 @@
         if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
             globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
             modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-            if (readActivities.size() > 0) {
-                for (ActivityRecord r : readActivities) {
-                    r.readUriPermissions.remove(this);
-                    if (r.readUriPermissions.size() == 0) {
-                        r.readUriPermissions = null;
-                    }
+            if (readOwners.size() > 0) {
+                for (UriPermissionOwner r : readOwners) {
+                    r.removeReadPermission(this);
                 }
-                readActivities.clear();
+                readOwners.clear();
             }
         }
         if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
             globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
             modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-            if (readActivities.size() > 0) {
-                for (ActivityRecord r : readActivities) {
-                    r.writeUriPermissions.remove(this);
-                    if (r.writeUriPermissions.size() == 0) {
-                        r.writeUriPermissions = null;
-                    }
+            if (readOwners.size() > 0) {
+                for (UriPermissionOwner r : writeOwners) {
+                    r.removeWritePermission(this);
                 }
-                readActivities.clear();
+                readOwners.clear();
             }
         }
     }
@@ -85,11 +87,17 @@
                 pw.print(" uid="); pw.print(uid); 
                 pw.print(" globalModeFlags=0x");
                 pw.println(Integer.toHexString(globalModeFlags));
-        if (readActivities.size() != 0) {
-            pw.print(prefix); pw.print("readActivities="); pw.println(readActivities);
+        if (readOwners.size() != 0) {
+            pw.print(prefix); pw.println("readOwners:");
+            for (UriPermissionOwner owner : readOwners) {
+                pw.print(prefix); pw.print("  * "); pw.println(owner);
+            }
         }
-        if (writeActivities.size() != 0) {
-            pw.print(prefix); pw.print("writeActivities="); pw.println(writeActivities);
+        if (writeOwners.size() != 0) {
+            pw.print(prefix); pw.println("writeOwners:");
+            for (UriPermissionOwner owner : writeOwners) {
+                pw.print(prefix); pw.print("  * "); pw.println(owner);
+            }
         }
     }
 }
diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java
new file mode 100644
index 0000000..99c82e6
--- /dev/null
+++ b/services/java/com/android/server/am/UriPermissionOwner.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+class UriPermissionOwner {
+    final ActivityManagerService service;
+    final Object owner;
+
+    Binder externalToken;
+
+    HashSet<UriPermission> readUriPermissions; // special access to reading uris.
+    HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
+
+    class ExternalToken extends Binder {
+        UriPermissionOwner getOwner() {
+            return UriPermissionOwner.this;
+        }
+    }
+
+    UriPermissionOwner(ActivityManagerService _service, Object _owner) {
+        service = _service;
+        owner = _owner;
+    }
+
+    Binder getExternalTokenLocked() {
+        if (externalToken != null) {
+            externalToken = new ExternalToken();
+        }
+        return externalToken;
+    }
+
+    static UriPermissionOwner fromExternalToken(IBinder token) {
+        if (token instanceof ExternalToken) {
+            return ((ExternalToken)token).getOwner();
+        }
+        return null;
+    }
+
+    void removeUriPermissionsLocked() {
+        removeUriPermissionsLocked(Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    void removeUriPermissionsLocked(int mode) {
+        if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
+                && readUriPermissions != null) {
+            for (UriPermission perm : readUriPermissions) {
+                perm.readOwners.remove(this);
+                if (perm.readOwners.size() == 0 && (perm.globalModeFlags
+                        &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+                    perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+                    service.removeUriPermissionIfNeededLocked(perm);
+                }
+            }
+            readUriPermissions = null;
+        }
+        if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+                && writeUriPermissions != null) {
+            for (UriPermission perm : writeUriPermissions) {
+                perm.writeOwners.remove(this);
+                if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
+                        &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+                    perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+                    service.removeUriPermissionIfNeededLocked(perm);
+                }
+            }
+            writeUriPermissions = null;
+        }
+    }
+
+    void removeUriPermissionLocked(Uri uri, int mode) {
+        if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
+                && readUriPermissions != null) {
+            Iterator<UriPermission> it = readUriPermissions.iterator();
+            while (it.hasNext()) {
+                UriPermission perm = it.next();
+                if (uri.equals(perm.uri)) {
+                    perm.readOwners.remove(this);
+                    if (perm.readOwners.size() == 0 && (perm.globalModeFlags
+                            &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+                        perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+                        service.removeUriPermissionIfNeededLocked(perm);
+                    }
+                    it.remove();
+                }
+            }
+            if (readUriPermissions.size() == 0) {
+                readUriPermissions = null;
+            }
+        }
+        if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+                && writeUriPermissions != null) {
+            Iterator<UriPermission> it = writeUriPermissions.iterator();
+            while (it.hasNext()) {
+                UriPermission perm = it.next();
+                if (uri.equals(perm.uri)) {
+                    perm.writeOwners.remove(this);
+                    if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
+                            &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+                        perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+                        service.removeUriPermissionIfNeededLocked(perm);
+                    }
+                    it.remove();
+                }
+            }
+            if (writeUriPermissions.size() == 0) {
+                writeUriPermissions = null;
+            }
+        }
+    }
+
+    public void addReadPermission(UriPermission perm) {
+        if (readUriPermissions == null) {
+            readUriPermissions = new HashSet<UriPermission>();
+        }
+        readUriPermissions.add(perm);
+    }
+
+    public void addWritePermission(UriPermission perm) {
+        if (writeUriPermissions == null) {
+            writeUriPermissions = new HashSet<UriPermission>();
+        }
+        writeUriPermissions.add(perm);
+    }
+
+    public void removeReadPermission(UriPermission perm) {
+        readUriPermissions.remove(perm);
+        if (readUriPermissions.size() == 0) {
+            readUriPermissions = null;
+        }
+    }
+
+    public void removeWritePermission(UriPermission perm) {
+        writeUriPermissions.remove(perm);
+        if (writeUriPermissions.size() == 0) {
+            writeUriPermissions = null;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return owner.toString();
+    }
+}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 949b874..53ce5c0 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -34,6 +34,7 @@
 import android.net.NetworkInfo;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -114,6 +115,14 @@
     private boolean mUsbMassStorageOff;  // track the status of USB Mass Storage
     private boolean mUsbConnected;       // track the status of USB connection
 
+    // mUsbHandler message
+    static final int USB_STATE_CHANGE = 1;
+    static final int USB_DISCONNECTED = 0;
+    static final int USB_CONNECTED = 1;
+
+    // Time to delay before processing USB disconnect events
+    static final long USB_DISCONNECT_DELAY = 1000;
+
     public Tethering(Context context, Looper looper) {
         Log.d(TAG, "Tethering starting");
         mContext = context;
@@ -453,12 +462,25 @@
         }
     }
 
+    private Handler mUsbHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            mUsbConnected = (msg.arg1 == USB_CONNECTED);
+            updateUsbStatus();
+        }
+    };
+
     private class StateReceiver extends BroadcastReceiver {
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
             if (action.equals(Usb.ACTION_USB_STATE)) {
-                mUsbConnected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
-                updateUsbStatus();
+                // process connect events immediately, but delay handling disconnects
+                // to debounce USB configuration changes
+                boolean connected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
+                Message msg = Message.obtain(mUsbHandler, USB_STATE_CHANGE,
+                        (connected ? USB_CONNECTED : USB_DISCONNECTED), 0);
+                mUsbHandler.removeMessages(USB_STATE_CHANGE);
+                mUsbHandler.sendMessageDelayed(msg, connected ? 0 : USB_DISCONNECT_DELAY);
             } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
                 mUsbMassStorageOff = false;
                 updateUsbStatus();
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
index 1142136..3dcaff6 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/services/java/com/android/server/sip/SipService.java
@@ -32,6 +32,7 @@
 import android.net.sip.SipSessionAdapter;
 import android.net.sip.SipSessionState;
 import android.net.wifi.WifiManager;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -97,6 +98,7 @@
     }
 
     public void open(SipProfile localProfile) {
+        localProfile.setCallingUid(Binder.getCallingUid());
         if (localProfile.getAutoRegistration()) {
             openToReceiveCalls(localProfile);
         } else {
@@ -119,6 +121,7 @@
 
     public synchronized void open3(SipProfile localProfile,
             String incomingCallBroadcastAction, ISipSessionListener listener) {
+        localProfile.setCallingUid(Binder.getCallingUid());
         if (TextUtils.isEmpty(incomingCallBroadcastAction)) {
             throw new RuntimeException(
                     "empty broadcast action for incoming call");
@@ -165,6 +168,7 @@
 
     public synchronized ISipSession createSession(SipProfile localProfile,
             ISipSessionListener listener) {
+        localProfile.setCallingUid(Binder.getCallingUid());
         if (!mConnected) return null;
         try {
             SipSessionGroupExt group = createGroup(localProfile);
@@ -362,16 +366,7 @@
 
         private SipProfile duplicate(SipProfile p) {
             try {
-                return new SipProfile.Builder(p.getUserName(), p.getSipDomain())
-                        .setProfileName(p.getProfileName())
-                        .setPassword("*")
-                        .setPort(p.getPort())
-                        .setProtocol(p.getProtocol())
-                        .setOutboundProxy(p.getProxyAddress())
-                        .setSendKeepAlive(p.getSendKeepAlive())
-                        .setAutoRegistration(p.getAutoRegistration())
-                        .setDisplayName(p.getDisplayName())
-                        .build();
+                return new SipProfile.Builder(p).setPassword("*").build();
             } catch (Exception e) {
                 Log.wtf(TAG, "duplicate()", e);
                 throw new RuntimeException("duplicate profile", e);
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
index db3f536..d33558b 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -381,14 +381,29 @@
                     : listener);
         }
 
+        // process the command in a new thread
+        private void doCommandAsync(final EventObject command) {
+            new Thread(new Runnable() {
+                    public void run() {
+                        try {
+                            processCommand(command);
+                        } catch (SipException e) {
+                            // TODO: find a better way to do this
+                            if ((command instanceof RegisterCommand)
+                                    || (command == DEREGISTER)) {
+                                onRegistrationFailed(e);
+                            } else {
+                                onError(e);
+                            }
+                        }
+                    }
+            }).start();
+        }
+
         public void makeCall(SipProfile peerProfile,
                 SessionDescription sessionDescription) {
-            try {
-                processCommand(
-                        new MakeCallCommand(peerProfile, sessionDescription));
-            } catch (SipException e) {
-                onError(e);
-            }
+            doCommandAsync(
+                    new MakeCallCommand(peerProfile, sessionDescription));
         }
 
         public void answerCall(SessionDescription sessionDescription) {
@@ -401,36 +416,20 @@
         }
 
         public void endCall() {
-            try {
-                processCommand(END_CALL);
-            } catch (SipException e) {
-                onError(e);
-            }
+            doCommandAsync(END_CALL);
         }
 
         public void changeCall(SessionDescription sessionDescription) {
-            try {
-                processCommand(
-                        new MakeCallCommand(mPeerProfile, sessionDescription));
-            } catch (SipException e) {
-                onError(e);
-            }
+            doCommandAsync(
+                    new MakeCallCommand(mPeerProfile, sessionDescription));
         }
 
         public void register(int duration) {
-            try {
-                processCommand(new RegisterCommand(duration));
-            } catch (SipException e) {
-                onRegistrationFailed(e);
-            }
+            doCommandAsync(new RegisterCommand(duration));
         }
 
         public void unregister() {
-            try {
-                processCommand(DEREGISTER);
-            } catch (SipException e) {
-                onRegistrationFailed(e);
-            }
+            doCommandAsync(DEREGISTER);
         }
 
         public boolean isReRegisterRequired() {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index ba58b43..3addc0d 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -139,6 +139,7 @@
     jmethodID filterJumpyTouchEvents;
     jmethodID getVirtualKeyDefinitions;
     jmethodID getExcludedDeviceNames;
+    jmethodID getMaxEventsPerSecond;
 } gCallbacksClassInfo;
 
 static struct {
@@ -249,6 +250,7 @@
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
     virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+    virtual int32_t getMaxEventsPerSecond();
 
 private:
     struct InputWindow {
@@ -310,6 +312,9 @@
     int32_t mFilterTouchEvents;
     int32_t mFilterJumpyTouchEvents;
 
+    // Cached throttling policy.
+    int32_t mMaxEventsPerSecond;
+
     // Cached display state.  (lock mDisplayLock)
     Mutex mDisplayLock;
     int32_t mDisplayWidth, mDisplayHeight;
@@ -400,6 +405,7 @@
 
 NativeInputManager::NativeInputManager(jobject callbacksObj) :
     mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
+    mMaxEventsPerSecond(-1),
     mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0),
     mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
     mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
@@ -921,6 +927,21 @@
     }
 }
 
+int32_t NativeInputManager::getMaxEventsPerSecond() {
+    if (mMaxEventsPerSecond < 0) {
+        JNIEnv* env = jniEnv();
+
+        jint result = env->CallIntMethod(mCallbacksObj,
+                gCallbacksClassInfo.getMaxEventsPerSecond);
+        if (checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) {
+            result = 60;
+        }
+
+        mMaxEventsPerSecond = result;
+    }
+    return mMaxEventsPerSecond;
+}
+
 void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
 #if DEBUG_FOCUS
     LOGD("setInputWindows");
@@ -2293,6 +2314,9 @@
     GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
             "getExcludedDeviceNames", "()[Ljava/lang/String;");
 
+    GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
+            "getMaxEventsPerSecond", "()I");
+
     // VirtualKeyDefinition
 
     FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 79772ed..e4825d0 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -22,7 +22,7 @@
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 
 ifeq ($(TARGET_BOARD_PLATFORM), omap3)
-	LOCAL_CFLAGS += -DNO_RGBX_8888
+	LOCAL_CFLAGS += -DNO_RGBX_8888 -DHAS_PUSH_BUFFERS
 endif
 
 # need "-lrt" on Linux simulator to pick up clock_gettime
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3720e1669..194c295 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -163,9 +163,13 @@
     const uint32_t hwFlags = hw.getFlags();
     
     mFormat = format;
-    mReqFormat = format;
     mWidth  = w;
     mHeight = h;
+
+    mReqFormat = format;
+    mReqWidth = w;
+    mReqHeight = h;
+
     mSecure = (flags & ISurfaceComposer::eSecure) ? true : false;
     mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;
 
@@ -253,12 +257,16 @@
     } else {
 slowpath:
         GGLSurface t;
-        status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
-        LOGE_IF(res, "error %d (%s) locking buffer %p",
-                res, strerror(res), buffer.get());
-        if (res == NO_ERROR) {
-            mBufferManager.loadTexture(dirty, t);
-            buffer->unlock();
+        if (buffer->usage & GRALLOC_USAGE_SW_READ_MASK) {
+            status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
+            LOGE_IF(res, "error %d (%s) locking buffer %p",
+                    res, strerror(res), buffer.get());
+            if (res == NO_ERROR) {
+                mBufferManager.loadTexture(dirty, t);
+                buffer->unlock();
+            }
+        } else {
+            // we can't do anything
         }
     }
 }
@@ -357,16 +365,22 @@
     uint32_t w, h, f;
     { // scope for the lock
         Mutex::Autolock _l(mLock);
-        const bool fixedSizeChanged = mFixedSize != (reqWidth && reqHeight);
-        const bool formatChanged    = mReqFormat != reqFormat;
-        mReqWidth  = reqWidth;
-        mReqHeight = reqHeight;
-        mReqFormat = reqFormat;
-        mFixedSize = reqWidth && reqHeight;
-        w = reqWidth  ? reqWidth  : mWidth;
-        h = reqHeight ? reqHeight : mHeight;
-        f = reqFormat ? reqFormat : mFormat;
-        if (fixedSizeChanged || formatChanged) {
+
+        // zero means default
+        if (!reqFormat) reqFormat = mFormat;
+        if (!reqWidth)  reqWidth = mWidth;
+        if (!reqHeight) reqHeight = mHeight;
+
+        w = reqWidth;
+        h = reqHeight;
+        f = reqFormat;
+
+        if ((reqWidth != mReqWidth) || (reqHeight != mReqHeight) ||
+                (reqFormat != mReqFormat)) {
+            mReqWidth  = reqWidth;
+            mReqHeight = reqHeight;
+            mReqFormat = reqFormat;
+
             lcblk->reallocateAllExcept(index);
         }
     }
@@ -530,9 +544,9 @@
         return;
     }
 
-    // get the dirty region
     sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));
     if (newFrontBuffer != NULL) {
+        // get the dirty region
         // compute the posted region
         const Region dirty(lcblk->getDirtyRegion(buf));
         mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );
@@ -568,6 +582,13 @@
             // we now have the correct size, unfreeze the screen
             mFreezeLock.clear();
         }
+
+        // get the crop region
+        setBufferCrop( lcblk->getCrop(buf) );
+
+        // get the transformation
+        setBufferTransform( lcblk->getTransform(buf) );
+
     } else {
         // this should not happen unless we ran out of memory while
         // allocating the buffer. we're hoping that things will get back
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 043d54d..3d049a7 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -54,6 +54,8 @@
 {
     const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
     mFlags = hw.getFlags();
+    mBufferCrop.makeInvalid();
+    mBufferTransform = 0;
 }
 
 LayerBase::~LayerBase()
@@ -354,6 +356,14 @@
     clearWithOpenGL(clip,0,0,0,0);
 }
 
+template <typename T>
+static inline
+void swap(T& a, T& b) {
+    T t(a);
+    a = b;
+    b = t;
+}
+
 void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
 {
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
@@ -387,37 +397,72 @@
         }
     }
 
-    Region::const_iterator it = clip.begin();
-    Region::const_iterator const end = clip.end();
-    const GLfloat texCoords[4][2] = {
-            { 0,  0 },
-            { 0,  1 },
-            { 1,  1 },
-            { 1,  0 }
+    /*
+     *  compute texture coordinates
+     *  here, we handle NPOT, cropping and buffer transformations
+     */
+
+    GLfloat cl, ct, cr, cb;
+    if (!mBufferCrop.isEmpty()) {
+        // source is cropped
+        const GLfloat us = (texture.NPOTAdjust ? texture.wScale : 1.0f) / width;
+        const GLfloat vs = (texture.NPOTAdjust ? texture.hScale : 1.0f) / height;
+        cl = mBufferCrop.left   * us;
+        ct = mBufferCrop.top    * vs;
+        cr = mBufferCrop.right  * us;
+        cb = mBufferCrop.bottom * vs;
+    } else {
+        cl = 0;
+        ct = 0;
+        cr = (texture.NPOTAdjust ? texture.wScale : 1.0f);
+        cb = (texture.NPOTAdjust ? texture.hScale : 1.0f);
+    }
+
+    struct TexCoords {
+        GLfloat u;
+        GLfloat v;
     };
 
-    glMatrixMode(GL_TEXTURE);
-    glLoadIdentity();
+    enum {
+        // name of the corners in the texture map
+        LB = 0, // left-bottom
+        LT = 1, // left-top
+        RT = 2, // right-top
+        RB = 3  // right-bottom
+    };
+
+    // vertices in screen space
+    int vLT = LB;
+    int vLB = LT;
+    int vRB = RT;
+    int vRT = RB;
 
     // the texture's source is rotated
-    switch (texture.transform) {
-        case HAL_TRANSFORM_ROT_90:
-            glTranslatef(0, 1, 0);
-            glRotatef(-90, 0, 0, 1);
-            break;
-        case HAL_TRANSFORM_ROT_180:
-            glTranslatef(1, 1, 0);
-            glRotatef(-180, 0, 0, 1);
-            break;
-        case HAL_TRANSFORM_ROT_270:
-            glTranslatef(1, 0, 0);
-            glRotatef(-270, 0, 0, 1);
-            break;
+    uint32_t transform = mBufferTransform;
+    if (transform & HAL_TRANSFORM_ROT_90) {
+        vLT = RB;
+        vLB = LB;
+        vRB = LT;
+        vRT = RT;
+    }
+    if (transform & HAL_TRANSFORM_FLIP_V) {
+        swap(vLT, vLB);
+        swap(vRB, vRT);
+    }
+    if (transform & HAL_TRANSFORM_FLIP_H) {
+        swap(vLT, vRB);
+        swap(vLB, vRT);
     }
 
-    if (texture.NPOTAdjust) {
-        glScalef(texture.wScale, texture.hScale, 1.0f);
-    }
+    TexCoords texCoords[4];
+    texCoords[vLT].u = cl;
+    texCoords[vLT].v = ct;
+    texCoords[vLB].u = cl;
+    texCoords[vLB].v = cb;
+    texCoords[vRB].u = cr;
+    texCoords[vRB].v = cb;
+    texCoords[vRT].u = cr;
+    texCoords[vRT].v = ct;
 
     if (needsDithering()) {
         glEnable(GL_DITHER);
@@ -429,6 +474,8 @@
     glVertexPointer(2, GL_FLOAT, 0, mVertices);
     glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
 
+    Region::const_iterator it = clip.begin();
+    Region::const_iterator const end = clip.end();
     while (it != end) {
         const Rect& r = *it++;
         const GLint sy = fbHeight - (r.top + r.height());
@@ -438,6 +485,16 @@
     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 }
 
+void LayerBase::setBufferCrop(const Rect& crop) {
+    if (!crop.isEmpty()) {
+        mBufferCrop = crop;
+    }
+}
+
+void LayerBase::setBufferTransform(uint32_t transform) {
+    mBufferTransform = transform;
+}
+
 void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
 {
     const Layer::State& s(drawingState());
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index dd1cd05..c66dc34 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -232,9 +232,17 @@
           void clearWithOpenGL(const Region& clip) const;
           void drawWithOpenGL(const Region& clip, const Texture& texture) const;
           
+          // these must be called from the post/drawing thread
+          void setBufferCrop(const Rect& crop);
+          void setBufferTransform(uint32_t transform);
+
                 sp<SurfaceFlinger> mFlinger;
                 uint32_t        mFlags;
 
+                // post/drawing thread
+                Rect mBufferCrop;
+                uint32_t mBufferTransform;
+
                 // cached during validateVisibility()
                 bool            mNeedsFiltering;
                 int32_t         mOrientation;
diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp
index 64a43c7..2ee21b9 100644
--- a/services/surfaceflinger/LayerBlur.cpp
+++ b/services/surfaceflinger/LayerBlur.cpp
@@ -241,6 +241,8 @@
             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
         }
         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+        glLoadIdentity();
+        glMatrixMode(GL_MODELVIEW);
     }
 }
 
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index e8b2ebf..fdf9abc8 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -485,7 +485,7 @@
         mTextureManager.loadTexture(&mTexture, dirty, t);
     }
 
-    mTexture.transform = mBufferHeap.transform;
+    mLayer.setBufferTransform(mBufferHeap.transform);
     mLayer.drawWithOpenGL(clip, mTexture);
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d257897..a78d9b9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -165,7 +165,7 @@
 {
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
-    LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );  
+    LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
     mBootFinished = true;
     property_set("ctl.stop", "bootanim");
 }
@@ -201,10 +201,10 @@
     mServerHeap = new MemoryHeapBase(4096,
             MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap");
     LOGE_IF(mServerHeap==0, "can't create shared memory dealer");
-    
+
     mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase());
     LOGE_IF(mServerCblk==0, "can't get to shared control block's address");
-    
+
     new(mServerCblk) surface_flinger_cblk_t;
 
     // initialize primary screen
@@ -233,7 +233,7 @@
 
     // Initialize OpenGL|ES
     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-    glPixelStorei(GL_PACK_ALIGNMENT, 4); 
+    glPixelStorei(GL_PACK_ALIGNMENT, 4);
     glEnableClientState(GL_VERTEX_ARRAY);
     glEnable(GL_SCISSOR_TEST);
     glShadeModel(GL_FLAT);
@@ -267,7 +267,7 @@
 
     // start boot animation
     property_set("ctl.start", "bootanim");
-    
+
     return NO_ERROR;
 }
 
@@ -662,7 +662,7 @@
 
         // Update aboveOpaqueLayers for next (lower) layer
         aboveOpaqueLayers.orSelf(opaqueRegion);
-        
+
         // Store the visible region is screen space
         layer->setVisibleRegion(visibleRegion);
         layer->setCoveredRegion(coveredRegion);
@@ -781,8 +781,8 @@
     glLoadIdentity();
 
     uint32_t flags = hw.getFlags();
-    if ((flags & DisplayHardware::SWAP_RECTANGLE) || 
-        (flags & DisplayHardware::BUFFER_PRESERVED)) 
+    if ((flags & DisplayHardware::SWAP_RECTANGLE) ||
+        (flags & DisplayHardware::BUFFER_PRESERVED))
     {
         // we can redraw only what's dirty, but since SWAP_RECTANGLE only
         // takes a rectangle, we must make sure to update that whole
@@ -1129,7 +1129,7 @@
     if (android_atomic_dec(&mTransactionCount) == 1) {
         signalEvent();
 
-        // if there is a transaction with a resize, wait for it to 
+        // if there is a transaction with a resize, wait for it to
         // take effect before returning.
         Mutex::Autolock _l(mStateLock);
         while (mResizeTransationPending) {
@@ -1173,7 +1173,7 @@
     return NO_ERROR;
 }
 
-int SurfaceFlinger::setOrientation(DisplayID dpy, 
+int SurfaceFlinger::setOrientation(DisplayID dpy,
         int orientation, uint32_t flags)
 {
     if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
@@ -1206,14 +1206,17 @@
                 int(w), int(h));
         return surfaceHandle;
     }
-    
+
     //LOGD("createSurface for pid %d (%d x %d)", pid, w, h);
     sp<Layer> normalLayer;
     switch (flags & eFXSurfaceMask) {
         case eFXSurfaceNormal:
+#if HAS_PUSH_BUFFERS
             if (UNLIKELY(flags & ePushBuffers)) {
                 layer = createPushBuffersSurface(client, d, w, h, flags);
-            } else {
+            } else
+#endif
+            {
                 normalLayer = createNormalSurface(client, d, w, h, flags, format);
                 layer = normalLayer;
             }
@@ -1232,7 +1235,7 @@
         ssize_t token = addClientLayer(client, layer);
 
         surfaceHandle = layer->getSurface();
-        if (surfaceHandle != 0) { 
+        if (surfaceHandle != 0) {
             params->token = token;
             params->identity = surfaceHandle->getIdentity();
             params->width = w;
@@ -1316,7 +1319,7 @@
     /*
      * called by the window manager, when a surface should be marked for
      * destruction.
-     * 
+     *
      * The surface is removed from the current and drawing lists, but placed
      * in the purgatory queue, so it's not destroyed right-away (we need
      * to wait for all client's references to go away first).
@@ -1337,7 +1340,7 @@
 status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer)
 {
     // called by ~ISurface() when all references are gone
-    
+
     class MessageDestroySurface : public MessageBase {
         SurfaceFlinger* flinger;
         sp<LayerBaseClient> layer;
@@ -1350,9 +1353,9 @@
             layer.clear(); // clear it outside of the lock;
             Mutex::Autolock _l(flinger->mStateLock);
             /*
-             * remove the layer from the current list -- chances are that it's 
-             * not in the list anyway, because it should have been removed 
-             * already upon request of the client (eg: window manager). 
+             * remove the layer from the current list -- chances are that it's
+             * not in the list anyway, because it should have been removed
+             * already upon request of the client (eg: window manager).
              * However, a buggy client could have not done that.
              * Since we know we don't have any more clients, we don't need
              * to use the purgatory.
@@ -1467,7 +1470,7 @@
         }
         const bool locked(retry >= 0);
         if (!locked) {
-            snprintf(buffer, SIZE, 
+            snprintf(buffer, SIZE,
                     "SurfaceFlinger appears to be unresponsive, "
                     "dumping anyways (no locks held)\n");
             result.append(buffer);
@@ -1742,12 +1745,15 @@
 {
     int32_t name = NAME_NOT_FOUND;
     sp<Layer> layer(mFlinger->getLayer(sur));
-    if (layer == 0) return name;
+    if (layer == 0) {
+        return name;
+    }
 
     // if this layer already has a token, just return it
     name = layer->getToken();
-    if ((name >= 0) && (layer->getClient() == this))
+    if ((name >= 0) && (layer->getClient() == this)) {
         return name;
+    }
 
     name = 0;
     do {
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
index 0f448e0..76f6159 100644
--- a/services/surfaceflinger/TextureManager.cpp
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -121,7 +121,6 @@
     case HAL_PIXEL_FORMAT_YCbCr_422_SP:
     case HAL_PIXEL_FORMAT_YCrCb_420_SP:
     case HAL_PIXEL_FORMAT_YCbCr_422_I:
-    case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
         return true;
     }
 
diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h
index c7c14e7..18c43486 100644
--- a/services/surfaceflinger/TextureManager.h
+++ b/services/surfaceflinger/TextureManager.h
@@ -40,12 +40,11 @@
 struct Image {
     enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 };
     Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0),
-        transform(0), dirty(1), target(TEXTURE_2D) { }
+        dirty(1), target(TEXTURE_2D) { }
     GLuint        name;
     EGLImageKHR   image;
     GLuint        width;
     GLuint        height;
-    uint32_t      transform;
     unsigned      dirty     : 1;
     unsigned      target    : 1;
 };
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 92bed8d..2370add 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -177,7 +177,7 @@
 
     /**
      * Returns the unique device ID, for example, the IMEI for GSM and the MEID
-     * for CDMA phones. Return null if device ID is not available.
+     * or ESN for CDMA phones. Return null if device ID is not available.
      *
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 8e08592..12df44e 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -322,7 +322,9 @@
                 }
                 break;
         }
-        audioManager.setMode(mode);
+        // calling audioManager.setMode() multiple times in a short period of
+        // time seems to break the audio recorder in in-call mode
+        if (audioManager.getMode() != mode) audioManager.setMode(mode);
     }
 
     private Context getContext() {
@@ -689,8 +691,8 @@
      * Stop the playing DTMF tone. Ignored if there is no playing DTMF
      * tone or no active call.
      */
-    public void stopDtmf(Phone phone) {
-        phone.stopDtmf();
+    public void stopDtmf() {
+        if (hasActiveFgCall()) getFgPhone().stopDtmf();
     }
 
     /**
@@ -707,7 +709,7 @@
      * @param onComplete is the callback message when the action is processed by BP
      *
      */
-    public boolean sendBurstDtmf(Phone phone, String dtmfString, int on, int off, Message onComplete) {
+    public boolean sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
         if (hasActiveFgCall()) {
             getActiveFgCall().getPhone().sendBurstDtmf(dtmfString, on, off, onComplete);
             return true;
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 13c76e5..7e722cb 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -21,11 +21,17 @@
 import com.android.internal.util.HierarchicalState;
 import com.android.internal.util.HierarchicalStateMachine;
 
+import android.net.NetworkProperties;
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemProperties;
 import android.util.EventLog;
 
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
 /**
  * {@hide}
  *
@@ -255,10 +261,7 @@
     protected int mTag;
     protected PhoneBase phone;
     protected int cid;
-    protected String interfaceName;
-    protected String ipAddress;
-    protected String gatewayAddress;
-    protected String[] dnsServers;
+    protected NetworkProperties mNetworkProperties = new NetworkProperties();
     protected long createTime;
     protected long lastFailTime;
     protected FailCause lastFailCause;
@@ -283,8 +286,6 @@
         if (DBG) log("DataConnection constructor E");
         this.phone = phone;
         this.cid = -1;
-        this.dnsServers = new String[2];
-
         clearSettings();
 
         setDbg(false);
@@ -377,11 +378,7 @@
         this.lastFailTime = -1;
         this.lastFailCause = FailCause.NONE;
 
-        interfaceName = null;
-        ipAddress = null;
-        gatewayAddress = null;
-        dnsServers[0] = null;
-        dnsServers[1] = null;
+        mNetworkProperties.clear();
     }
 
     /**
@@ -416,37 +413,65 @@
 //            for (int i = 0; i < response.length; i++) {
 //                log("  response[" + i + "]='" + response[i] + "'");
 //            }
+
+            // Start with clean network properties and if we have
+            // a failure we'll clear again at the bottom of this code.
+            mNetworkProperties.clear();
             if (response.length >= 2) {
                 cid = Integer.parseInt(response[0]);
-                interfaceName = response[1];
+                String interfaceName = response[1];
+                result = SetupResult.SUCCESS;
 
-                String prefix = "net." + interfaceName + ".";
-                gatewayAddress = SystemProperties.get(prefix + "gw");
-                dnsServers[0] = SystemProperties.get(prefix + "dns1");
-                dnsServers[1] = SystemProperties.get(prefix + "dns2");
+                try {
+                    String prefix = "net." + interfaceName + ".";
 
-                if (response.length > 2) {
-                    ipAddress = response[2];
+                    mNetworkProperties.setInterface(NetworkInterface.getByName(interfaceName));
 
-                    if (isDnsOk(dnsServers)) {
-                        result = SetupResult.SUCCESS;
-                    } else {
-                        result = SetupResult.ERR_BadDns;
+                    // TODO: Get gateway and dns via RIL interface not property?
+                    String gatewayAddress = SystemProperties.get(prefix + "gw");
+                    mNetworkProperties.setGateway(InetAddress.getByName(gatewayAddress));
+
+                    if (response.length > 2) {
+                        String ipAddress = response[2];
+                        mNetworkProperties.addAddress(InetAddress.getByName(ipAddress));
+
+                        // TODO: Get gateway and dns via RIL interface not property?
+                        String dnsServers[] = new String[2];
+                        dnsServers[0] = SystemProperties.get(prefix + "dns1");
+                        dnsServers[1] = SystemProperties.get(prefix + "dns2");
+                        if (isDnsOk(dnsServers)) {
+                            mNetworkProperties.addDns(InetAddress.getByName(dnsServers[0]));
+                            mNetworkProperties.addDns(InetAddress.getByName(dnsServers[1]));
+                        } else {
+                            result = SetupResult.ERR_BadDns;
+                        }
                     }
-                } else {
-                    result = SetupResult.SUCCESS;
+                } catch (UnknownHostException e1) {
+                    log("onSetupCompleted: UnknowHostException " + e1);
+                    e1.printStackTrace();
+                    result = SetupResult.ERR_Other;
+                } catch (SocketException e2) {
+                    log("onSetupCompleted: SocketException " + e2);
+                    e2.printStackTrace();
+                    result = SetupResult.ERR_Other;
                 }
             } else {
+                log("onSetupCompleted: error; expected number of responses >= 2 was " +
+                        response.length);
                 result = SetupResult.ERR_Other;
             }
+
+            // An error occurred so clear properties
+            if (result != SetupResult.SUCCESS) {
+                log("onSetupCompleted with an error clearing NetworkProperties");
+                mNetworkProperties.clear();
+            }
         }
 
         if (DBG) {
             log("DataConnection setup result='" + result + "' on cid=" + cid);
             if (result == SetupResult.SUCCESS) {
-                log("interface=" + interfaceName + " ipAddress=" + ipAddress
-                        + " gateway=" + gatewayAddress + " DNS1=" + dnsServers[0]
-                        + " DNS2=" + dnsServers[1]);
+                log("NetworkProperties: " + mNetworkProperties.toString());
             }
         }
         return result;
@@ -610,7 +635,16 @@
                             break;
                         case ERR_BadDns:
                             // Connection succeeded but DNS info is bad so disconnect
-                            EventLog.writeEvent(EventLogTags.PDP_BAD_DNS_ADDRESS, dnsServers[0]);
+                            StringBuilder dnsAddressesSb = new StringBuilder();
+                            for (InetAddress addr : mNetworkProperties.getDnses()) {
+                                if (dnsAddressesSb.length() != 0) dnsAddressesSb.append(" ");
+                                dnsAddressesSb.append(addr.toString());
+                            }
+                            if (dnsAddressesSb.length() == 0) {
+                                dnsAddressesSb.append("no-dns-addresses");
+                            }
+                            EventLog.writeEvent(EventLogTags.PDP_BAD_DNS_ADDRESS,
+                                    dnsAddressesSb.toString());
                             tearDownData(cp);
                             transitionTo(mDisconnectingBadDnsState);
                             break;
@@ -877,31 +911,10 @@
     }
 
     /**
-     * @return the interface name as a string.
+     * @return the connections NetworkProperties
      */
-    public String getInterface() {
-        return interfaceName;
-    }
-
-    /**
-     * @return the ip address as a string.
-     */
-    public String getIpAddress() {
-        return ipAddress;
-    }
-
-    /**
-     * @return the gateway address as a string.
-     */
-    public String getGatewayAddress() {
-        return gatewayAddress;
-    }
-
-    /**
-     * @return an array of associated DNS addresses.
-     */
-    public String[] getDnsServers() {
-        return dnsServers;
+    public NetworkProperties getNetworkProperties() {
+        return mNetworkProperties;
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 06807c6..14cb584 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -21,18 +21,14 @@
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
-import android.os.RemoteException;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
 import android.util.Log;
 
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 
+
 /**
  * {@hide}
  *
@@ -422,14 +418,6 @@
 
     public abstract ArrayList<DataConnection> getAllDataConnections();
 
-    protected abstract String getInterfaceName(String apnType);
-
-    protected abstract String getIpAddress(String apnType);
-
-    protected abstract String getGateway(String apnType);
-
-    protected abstract String[] getDnsServers(String apnType);
-
     protected abstract void setState(State s);
 
     protected NetworkProperties getNetworkProperties(String apnType) {
@@ -685,43 +673,7 @@
         }
     }
 
-    protected NetworkProperties makeNetworkProperties(DataConnection connection) {
-        NetworkProperties properties = new NetworkProperties();
-        try {
-            properties.setInterface(NetworkInterface.getByName(connection.getInterface()));
-        } catch (SocketException e) {
-            Log.e(LOG_TAG, "SocketException creating NetworkInterface: " + e);
-        } catch (NullPointerException e) {
-            Log.e(LOG_TAG, "NPE trying to makeNetworkProperties: " + e);
-        }
-
-        try {
-            properties.addAddress(InetAddress.getByName(connection.getIpAddress()));
-        } catch (UnknownHostException e) {
-            Log.e(LOG_TAG, "UnknownHostException setting IpAddress: " + e);
-        } catch (SecurityException e) {
-            Log.e(LOG_TAG, "SecurityException setting IpAddress: " + e);
-        }
-
-        try {
-            properties.setGateway(InetAddress.getByName(connection.getGatewayAddress()));
-        } catch (UnknownHostException e) {
-            Log.e(LOG_TAG, "UnknownHostException setting GatewayAddress: " + e);
-        } catch (SecurityException e) {
-            Log.e(LOG_TAG, "SecurityException setting GatewayAddress: " + e);
-        }
-
-        try {
-            String[] dnsStrings = connection.getDnsServers();
-            for (int i = 0; i<dnsStrings.length; i++) {
-                properties.addDns(InetAddress.getByName(dnsStrings[i]));
-            }
-        } catch (UnknownHostException e) {
-            Log.e(LOG_TAG, "UnknownHostException setting DnsAddress: " + e);
-        } catch (SecurityException e) {
-            Log.e(LOG_TAG, "SecurityException setting DnsAddress: " + e);
-        }
-        // TODO - set Proxy info
-        return properties;
+    protected NetworkProperties getNetworkProperties(DataConnection connection) {
+        return connection.getNetworkProperties();
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 784e87d..109b4b9 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -17,11 +17,9 @@
 package com.android.internal.telephony;
 
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.net.NetworkProperties;
 import android.os.Handler;
 import android.os.Message;
-import android.preference.PreferenceManager;
 import android.telephony.CellLocation;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
@@ -29,7 +27,6 @@
 
 import com.android.internal.telephony.DataConnection;
 import com.android.internal.telephony.gsm.NetworkInfo;
-import com.android.internal.telephony.gsm.GsmDataConnection;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 
 import java.util.List;
@@ -1382,29 +1379,6 @@
     boolean isDataConnectivityPossible();
 
     /**
-     * Returns the name of the network interface used by the specified APN type.
-     */
-    String getInterfaceName(String apnType);
-
-    /**
-     * Returns the IP address of the network interface used by the specified
-     * APN type.
-     */
-    String getIpAddress(String apnType);
-
-    /**
-     * Returns the gateway for the network interface used by the specified APN
-     * type.
-     */
-    String getGateway(String apnType);
-
-    /**
-     * Returns the DNS servers for the network interface used by the specified
-     * APN type.
-     */
-    public String[] getDnsServers(String apnType);
-
-    /**
      * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
      */
     String getDeviceId();
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index e5968a7..27e1cb3 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -36,10 +36,8 @@
 import android.util.Log;
 
 import com.android.internal.R;
-import com.android.internal.telephony.gsm.GsmDataConnection;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 
-import java.util.List;
 import java.util.Locale;
 
 
@@ -932,26 +930,10 @@
          logUnexpectedCdmaMethodCall("unsetOnEcbModeExitResponse");
      }
 
-    public String getInterfaceName(String apnType) {
-        return mDataConnection.getInterfaceName(apnType);
-    }
-
-    public String getIpAddress(String apnType) {
-        return mDataConnection.getIpAddress(apnType);
-    }
-
     public boolean isDataConnectivityEnabled() {
         return mDataConnection.getDataEnabled();
     }
 
-    public String getGateway(String apnType) {
-        return mDataConnection.getGateway(apnType);
-    }
-
-    public String[] getDnsServers(String apnType) {
-        return mDataConnection.getDnsServers(apnType);
-    }
-
     public String[] getActiveApnTypes() {
         return mDataConnection.getActiveApnTypes();
     }
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index d84859c..c10596d 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -20,14 +20,11 @@
 import android.app.ActivityManagerNative;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.net.NetworkProperties;
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemProperties;
-import android.preference.PreferenceManager;
 import android.telephony.CellLocation;
-import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.util.Log;
@@ -35,7 +32,6 @@
 import com.android.internal.telephony.cdma.CDMAPhone;
 import com.android.internal.telephony.gsm.GSMPhone;
 import com.android.internal.telephony.gsm.NetworkInfo;
-import com.android.internal.telephony.gsm.GsmDataConnection;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 
 import java.util.List;
@@ -669,22 +665,6 @@
         return mActivePhone.isDataConnectivityPossible();
     }
 
-    public String getInterfaceName(String apnType) {
-        return mActivePhone.getInterfaceName(apnType);
-    }
-
-    public String getIpAddress(String apnType) {
-        return mActivePhone.getIpAddress(apnType);
-    }
-
-    public String getGateway(String apnType) {
-        return mActivePhone.getGateway(apnType);
-    }
-
-    public String[] getDnsServers(String apnType) {
-        return mActivePhone.getDnsServers(apnType);
-    }
-
     public String getDeviceId() {
         return mActivePhone.getDeviceId();
     }
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 9e17eb1..f1a7107 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -44,10 +44,6 @@
 import android.util.Log;
 import android.view.WindowManager;
 
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.SmsResponse;
-import com.android.internal.telephony.WapPushOverSms;
 import com.android.internal.util.HexDump;
 
 import java.io.ByteArrayOutputStream;
@@ -75,7 +71,7 @@
     private static final int DEFAULT_SMS_MAX_COUNT = 100;
 
     /** Default timeout for SMS sent query */
-    private static final int DEFAULT_SMS_TIMOUEOUT = 6000;
+    private static final int DEFAULT_SMS_TIMEOUT = 6000;
 
     protected static final String[] RAW_PROJECTION = new String[] {
         "pdu",
@@ -83,8 +79,6 @@
         "destination_port",
     };
 
-    static final int MAIL_SEND_SMS = 1;
-
     static final protected int EVENT_NEW_SMS = 1;
 
     static final protected int EVENT_SEND_SMS_COMPLETE = 2;
@@ -154,10 +148,6 @@
      */
     private final int WAKE_LOCK_TIMEOUT = 5000;
 
-    private static SmsMessage mSmsMessage;
-    private static SmsMessageBase mSmsMessageBase;
-    private SmsMessageBase.SubmitPduBase mSubmitPduBase;
-
     protected boolean mStorageAvailable = true;
     protected boolean mReportMemoryStatusPending = false;
 
@@ -838,7 +828,7 @@
 
         mSTrackers.add(tracker);
         sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d),
-                DEFAULT_SMS_TIMOUEOUT);
+                DEFAULT_SMS_TIMEOUT);
     }
 
     protected String getAppNameByIntent(PendingIntent intent) {
@@ -932,7 +922,7 @@
     }
 
     /**
-     * Keeps track of an SMS that has been sent to the RIL, until it it has
+     * Keeps track of an SMS that has been sent to the RIL, until it has
      * successfully been sent, or we're done trying.
      *
      */
@@ -973,27 +963,26 @@
             }
         };
 
-        private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) {
-                    mStorageAvailable = false;
-                    mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
-                } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) {
-                    mStorageAvailable = true;
-                    mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
-                } else {
-                    // Assume the intent is one of the SMS receive intents that
-                    // was sent as an ordered broadcast.  Check result and ACK.
-                    int rc = getResultCode();
-                    boolean success = (rc == Activity.RESULT_OK)
-                                        || (rc == Intents.RESULT_SMS_HANDLED);
+    private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) {
+                mStorageAvailable = false;
+                mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+            } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) {
+                mStorageAvailable = true;
+                mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+            } else {
+                // Assume the intent is one of the SMS receive intents that
+                // was sent as an ordered broadcast.  Check result and ACK.
+                int rc = getResultCode();
+                boolean success = (rc == Activity.RESULT_OK)
+                        || (rc == Intents.RESULT_SMS_HANDLED);
 
-                    // For a multi-part message, this only ACKs the last part.
-                    // Previous parts were ACK'd as they were received.
-                    acknowledgeLastIncomingSms(success, rc, null);
-                }
+                // For a multi-part message, this only ACKs the last part.
+                // Previous parts were ACK'd as they were received.
+                acknowledgeLastIncomingSms(success, rc, null);
             }
-
-        };
+        }
+    };
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 0c3c534..8d77f76 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -224,6 +224,7 @@
             mCM.unregisterForNVReady(this); //EVENT_NV_READY
             mSST.unregisterForNetworkAttach(this); //EVENT_REGISTERED_TO_NETWORK
             mCM.unSetOnSuppServiceNotification(this);
+            removeCallbacks(mExitEcmRunnable);
 
             mPendingMmis.clear();
 
@@ -256,6 +257,7 @@
             this.mSST = null;
             this.mEriManager = null;
             this.mCcatService = null;
+            this.mExitEcmRunnable = null;
     }
 
     protected void finalize() {
@@ -452,9 +454,14 @@
         return mMeid;
     }
 
-    //returns MEID in CDMA
+    //returns MEID or ESN in CDMA
     public String getDeviceId() {
-        return getMeid();
+        String id = getMeid();
+        if ((id == null) || id.matches("^0*$")) {
+            Log.d(LOG_TAG, "getDeviceId(): MEID is not initialized use ESN");
+            id = getEsn();
+        }
+        return id;
     }
 
     public String getDeviceSvn() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 8a3af3b..c94cfa4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -23,14 +23,12 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.TrafficStats;
 import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
 import android.os.Message;
-import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -50,10 +48,7 @@
 import com.android.internal.telephony.EventLogTags;
 import com.android.internal.telephony.gsm.ApnSetting;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.ServiceStateTracker;
 
-import java.net.NetworkInterface;
 import java.util.ArrayList;
 
 /**
@@ -737,7 +732,7 @@
         }
 
         if (ar.exception == null) {
-            mNetworkProperties = makeNetworkProperties(mActiveDataConnection);
+            mNetworkProperties = getNetworkProperties(mActiveDataConnection);
 
             // everything is setup
             notifyDefaultData(reason);
@@ -967,34 +962,6 @@
         }
     }
 
-    protected String getInterfaceName(String apnType) {
-        if (mActiveDataConnection != null) {
-            return mActiveDataConnection.getInterface();
-        }
-        return null;
-    }
-
-    protected String getIpAddress(String apnType) {
-        if (mActiveDataConnection != null) {
-            return mActiveDataConnection.getIpAddress();
-        }
-        return null;
-    }
-
-    protected String getGateway(String apnType) {
-        if (mActiveDataConnection != null) {
-            return mActiveDataConnection.getGatewayAddress();
-        }
-        return null;
-    }
-
-    protected String[] getDnsServers(String apnType) {
-        if (mActiveDataConnection != null) {
-            return mActiveDataConnection.getDnsServers();
-        }
-        return null;
-    }
-
     public ArrayList<DataConnection> getAllDataConnections() {
         return dataConnectionList;
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 1b08aed..53d0f20 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -28,21 +28,20 @@
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemProperties;
+import android.preference.PreferenceManager;
 import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
-import android.preference.PreferenceManager;
-import android.util.Config;
-import android.util.Log;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage.MessageClass;
+import android.util.Config;
+import android.util.Log;
 
-import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
-import com.android.internal.telephony.cdma.SmsMessage;
+import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
 import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.util.HexDump;
@@ -56,14 +55,11 @@
 final class CdmaSMSDispatcher extends SMSDispatcher {
     private static final String TAG = "CDMA";
 
-    private CDMAPhone mCdmaPhone;
-
     private byte[] mLastDispatchedSmsFingerprint;
     private byte[] mLastAcknowledgedSmsFingerprint;
 
     CdmaSMSDispatcher(CDMAPhone phone) {
         super(phone);
-        mCdmaPhone = phone;
     }
 
     /**
@@ -130,7 +126,7 @@
             Log.d(TAG, "Voicemail count=" + voicemailCount);
             // Store the voicemail count in preferences.
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
-                    ((CDMAPhone) mPhone).getContext());
+                    mPhone.getContext());
             SharedPreferences.Editor editor = sp.edit();
             editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
             editor.commit();
@@ -445,7 +441,7 @@
     protected void sendSms(SmsTracker tracker) {
         HashMap<String, Object> map = tracker.mData;
 
-        byte smsc[] = (byte[]) map.get("smsc");
+        // byte smsc[] = (byte[]) map.get("smsc");  // unused for CDMA
         byte pdu[] = (byte[]) map.get("pdu");
 
         Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
index 09d46dd..1572f09 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
@@ -180,8 +180,8 @@
 
     @Override
     protected boolean isDnsOk(String[] domainNameServers) {
-        if (NULL_IP.equals(dnsServers[0]) && NULL_IP.equals(dnsServers[1])
-                    && !((GSMPhone) phone).isDnsCheckDisabled()) {
+        if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1])
+                && !((GSMPhone) phone).isDnsCheckDisabled()) {
             // Work around a race condition where QMI does not fill in DNS:
             // Deactivate PDP and let DataConnectionTracker retry.
             // Do not apply the race condition workaround for MMS APN
@@ -189,6 +189,9 @@
             // Otherwise, the default APN will not be restored anymore.
             if (!apn.types[0].equals(Phone.APN_TYPE_MMS)
                 || !isIpAddress(apn.mmsProxy)) {
+                log(String.format(
+                        "isDnsOk: return false apn.types[0]=%s APN_TYPE_MMS=%s isIpAddress(%s)=%s",
+                        apn.types[0], Phone.APN_TYPE_MMS, apn.mmsProxy, isIpAddress(apn.mmsProxy)));
                 return false;
             }
         }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index c76da80..face581 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -27,17 +27,14 @@
 import android.content.SharedPreferences;
 import android.database.ContentObserver;
 import android.database.Cursor;
-import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.NetworkInfo;
-import android.net.NetworkProperties;
 import android.net.ProxyProperties;
 import android.net.TrafficStats;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
 import android.os.Message;
-import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -61,7 +58,6 @@
 
 import java.io.IOException;
 import java.net.InetAddress;
-import java.net.NetworkInterface;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 
@@ -612,42 +608,6 @@
         return true;
     }
 
-    protected String getInterfaceName(String apnType) {
-        if (mActivePdp != null &&
-                (apnType == null ||
-                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
-            return mActivePdp.getInterface();
-        }
-        return null;
-    }
-
-    protected String getIpAddress(String apnType) {
-        if (mActivePdp != null &&
-                (apnType == null ||
-                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
-            return mActivePdp.getIpAddress();
-        }
-        return null;
-    }
-
-    public String getGateway(String apnType) {
-        if (mActivePdp != null &&
-                (apnType == null ||
-                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
-            return mActivePdp.getGatewayAddress();
-        }
-        return null;
-    }
-
-    protected String[] getDnsServers(String apnType) {
-        if (mActivePdp != null &&
-                (apnType == null ||
-                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
-            return mActivePdp.getDnsServers();
-        }
-        return null;
-    }
-
     private boolean
     pdpStatesHasCID (ArrayList<DataCallState> states, int cid) {
         for (int i = 0, s = states.size() ; i < s ; i++) {
@@ -1138,7 +1098,7 @@
         }
 
         if (ar.exception == null) {
-            mNetworkProperties = makeNetworkProperties(mActivePdp);
+            mNetworkProperties = getNetworkProperties(mActivePdp);
 
             ApnSetting apn = mActivePdp.getApn();
             if (apn.proxy != null && apn.proxy.length() != 0) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index aa16fa3..fe7a5cb 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -44,6 +44,13 @@
 
     //***** Constants
 
+    // Max Size of the Short Code (aka Short String from TS 22.030 6.5.2)
+    static final int MAX_LENGTH_SHORT_CODE = 2;
+
+    // TS 22.030 6.5.2 Every Short String USSD command will end with #-key
+    // (known as #-String)
+    static final char END_OF_USSD_COMMAND = '#';
+
     // From TS 22.030 6.5.2
     static final String ACTION_ACTIVATE = "*";
     static final String ACTION_DEACTIVATE = "#";
@@ -446,22 +453,69 @@
     }
 
     /**
-     * Helper function for newFromDialString.  Returns true if dialString appears to be a short code
-     * AND conditions are correct for it to be treated as such.
+     * Helper function for newFromDialString. Returns true if dialString appears
+     * to be a short code AND conditions are correct for it to be treated as
+     * such.
      */
     static private boolean isShortCode(String dialString, GSMPhone phone) {
         // Refer to TS 22.030 Figure 3.5.3.2:
-        // A 1 or 2 digit "short code" is treated as USSD if it is entered while on a call or
-        // does not satisfy the condition (exactly 2 digits && starts with '1').
-        return ((dialString != null && dialString.length() <= 2)
-                && !PhoneNumberUtils.isEmergencyNumber(dialString)
-                && (phone.isInCall()
-                    || !((dialString.length() == 2 && dialString.charAt(0) == '1')
-                         /* While contrary to TS 22.030, there is strong precedence
-                          * for treating "0" and "00" as call setup strings.
-                          */
-                         || dialString.equals("0")
-                         || dialString.equals("00"))));
+        if (dialString == null) {
+            return false;
+        }
+
+        // Illegal dial string characters will give a ZERO length.
+        // At this point we do not want to crash as any application with
+        // call privileges may send a non dial string.
+        // It return false as when the dialString is equal to NULL.
+        if (dialString.length() == 0) {
+            return false;
+        }
+
+        if (PhoneNumberUtils.isEmergencyNumber(dialString)) {
+            return false;
+        } else {
+            return isShortCodeUSSD(dialString, phone);
+        }
+    }
+
+    /**
+     * Helper function for isShortCode. Returns true if dialString appears to be
+     * a short code and it is a USSD structure
+     *
+     * According to the 3PGG TS 22.030 specification Figure 3.5.3.2: A 1 or 2
+     * digit "short code" is treated as USSD if it is entered while on a call or
+     * does not satisfy the condition (exactly 2 digits && starts with '1'), there
+     * are however exceptions to this rule (see below)
+     *
+     * Exception (1) to Call initiation is: If the user of the device is already in a call
+     * and enters a Short String without any #-key at the end and the length of the Short String is
+     * equal or less then the MAX_LENGTH_SHORT_CODE [constant that is equal to 2]
+     *
+     * The phone shall initiate a USSD/SS commands.
+     *
+     * Exception (2) to Call initiation is: If the user of the device enters one
+     * Digit followed by the #-key. This rule defines this String as the
+     * #-String which is a USSD/SS command.
+     *
+     * The phone shall initiate a USSD/SS command.
+     */
+    static private boolean isShortCodeUSSD(String dialString, GSMPhone phone) {
+        if (dialString != null) {
+            if (phone.isInCall()) {
+                // The maximum length of a Short Code (aka Short String) is 2
+                if (dialString.length() <= MAX_LENGTH_SHORT_CODE) {
+                    return true;
+                }
+            }
+
+            // The maximum length of a Short Code (aka Short String) is 2
+            if (dialString.length() <= MAX_LENGTH_SHORT_CODE) {
+                if (dialString.charAt(dialString.length() - 1) == END_OF_USSD_COMMAND) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index f09ff06..ed7066b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -27,13 +27,12 @@
 import android.util.Config;
 import android.util.Log;
 
-import com.android.internal.telephony.IccUtils;
-import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
-import com.android.internal.telephony.gsm.SmsMessage;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -99,20 +98,20 @@
         if (sms.isTypeZero()) {
             // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be
             // Displayed/Stored/Notified. They should only be acknowledged.
-            Log.d(TAG, "Received short message type 0, Dont display or store it. Send Ack");
+            Log.d(TAG, "Received short message type 0, Don't display or store it. Send Ack");
             return Intents.RESULT_SMS_HANDLED;
         }
 
         // Special case the message waiting indicator messages
         if (sms.isMWISetMessage()) {
             mGsmPhone.updateMessageWaitingIndicator(true);
-            handled |= sms.isMwiDontStore();
+            handled = sms.isMwiDontStore();
             if (Config.LOGD) {
                 Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
             }
         } else if (sms.isMWIClearMessage()) {
             mGsmPhone.updateMessageWaitingIndicator(false);
-            handled |= sms.isMwiDontStore();
+            handled = sms.isMwiDontStore();
             if (Config.LOGD) {
                 Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
             }
@@ -306,7 +305,7 @@
             map.put("smsc", pdus.encodedScAddress);
             map.put("pdu", pdus.encodedMessage);
 
-            SmsTracker tracker =  SmsTrackerFactory(map, sentIntent, deliveryIntent);
+            SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent);
             sendSms(tracker);
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
index ad05b82..f5d84eb 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
@@ -85,8 +85,10 @@
     protected void setState(Call.State state) {
         switch (state) {
             case ACTIVE:
-                connectTimeReal = SystemClock.elapsedRealtime();
-                connectTime = System.currentTimeMillis();
+                if (connectTime == 0) {
+                    connectTimeReal = SystemClock.elapsedRealtime();
+                    connectTime = System.currentTimeMillis();
+                }
                 break;
             case DISCONNECTED:
                 duration = SystemClock.elapsedRealtime() - connectTimeReal;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 81dfaeb..9a45d3a 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -107,7 +107,7 @@
     }
 
     public String getPhoneName() {
-        return mProfile.getProfileName();
+        return "SIP:" + getUriString(mProfile);
     }
 
     public String getSipUri() {
@@ -225,21 +225,25 @@
     }
 
     public void conference() throws CallStateException {
-        if ((foregroundCall.getState() != SipCall.State.ACTIVE)
-                || (foregroundCall.getState() != SipCall.State.ACTIVE)) {
-            throw new CallStateException("wrong state to merge calls: fg="
-                    + foregroundCall.getState() + ", bg="
-                    + backgroundCall.getState());
+        synchronized (SipPhone.class) {
+            if ((foregroundCall.getState() != SipCall.State.ACTIVE)
+                    || (foregroundCall.getState() != SipCall.State.ACTIVE)) {
+                throw new CallStateException("wrong state to merge calls: fg="
+                        + foregroundCall.getState() + ", bg="
+                        + backgroundCall.getState());
+            }
+            foregroundCall.merge(backgroundCall);
         }
-        foregroundCall.merge(backgroundCall);
     }
 
     public void conference(Call that) throws CallStateException {
-        if (!(that instanceof SipCall)) {
-            throw new CallStateException("expect " + SipCall.class
-                    + ", cannot merge with " + that.getClass());
+        synchronized (SipPhone.class) {
+            if (!(that instanceof SipCall)) {
+                throw new CallStateException("expect " + SipCall.class
+                        + ", cannot merge with " + that.getClass());
+            }
+            foregroundCall.merge((SipCall) that);
         }
-        foregroundCall.merge((SipCall) that);
     }
 
     public boolean canTransfer() {
@@ -251,12 +255,14 @@
     }
 
     public void clearDisconnected() {
-        ringingCall.clearDisconnected();
-        foregroundCall.clearDisconnected();
-        backgroundCall.clearDisconnected();
+        synchronized (SipPhone.class) {
+            ringingCall.clearDisconnected();
+            foregroundCall.clearDisconnected();
+            backgroundCall.clearDisconnected();
 
-        updatePhoneState();
-        notifyPreciseCallStateChanged();
+            updatePhoneState();
+            notifyPreciseCallStateChanged();
+        }
     }
 
     public void sendDtmf(char c) {
@@ -264,7 +270,9 @@
             Log.e(LOG_TAG,
                     "sendDtmf called with invalid character '" + c + "'");
         } else if (foregroundCall.getState().isAlive()) {
-            foregroundCall.sendDtmf(c);
+            synchronized (SipPhone.class) {
+                foregroundCall.sendDtmf(c);
+            }
         }
     }
 
@@ -310,7 +318,9 @@
     }
 
     public void setMute(boolean muted) {
-        foregroundCall.setMute(muted);
+        synchronized (SipPhone.class) {
+            foregroundCall.setMute(muted);
+        }
     }
 
     public boolean getMute() {
@@ -413,18 +423,20 @@
 
         @Override
         public void hangup() throws CallStateException {
-            Log.v(LOG_TAG, "hang up call: " + getState() + ": " + this
-                    + " on phone " + getPhone());
-            CallStateException excp = null;
-            for (Connection c : connections) {
-                try {
-                    c.hangup();
-                } catch (CallStateException e) {
-                    excp = e;
+            synchronized (SipPhone.class) {
+                Log.v(LOG_TAG, "hang up call: " + getState() + ": " + this
+                        + " on phone " + getPhone());
+                CallStateException excp = null;
+                for (Connection c : connections) {
+                    try {
+                        c.hangup();
+                    } catch (CallStateException e) {
+                        excp = e;
+                    }
                 }
+                if (excp != null) throw excp;
+                setState(State.DISCONNECTING);
             }
-            if (excp != null) throw excp;
-            setState(State.DISCONNECTING);
         }
 
         void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) {
@@ -457,19 +469,20 @@
         }
 
         void hold() throws CallStateException {
-            AudioGroup audioGroup = getAudioGroup();
-            if (audioGroup == null) return;
-            audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
             setState(State.HOLDING);
+            AudioGroup audioGroup = getAudioGroup();
+            if (audioGroup != null) {
+                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+            }
             for (Connection c : connections) ((SipConnection) c).hold();
         }
 
         void unhold() throws CallStateException {
-            AudioGroup audioGroup = getAudioGroup();
-            if (audioGroup == null) return;
-            audioGroup.setMode(AudioGroup.MODE_NORMAL);
             setState(State.ACTIVE);
-            for (Connection c : connections) ((SipConnection) c).unhold();
+            AudioGroup audioGroup = new AudioGroup();
+            for (Connection c : connections) {
+                ((SipConnection) c).unhold(audioGroup);
+            }
         }
 
         void setMute(boolean muted) {
@@ -486,17 +499,26 @@
         }
 
         void merge(SipCall that) throws CallStateException {
-            AudioGroup myGroup = getAudioGroup();
+            AudioGroup audioGroup = getAudioGroup();
             for (Connection c : that.connections) {
                 SipConnection conn = (SipConnection) c;
-                conn.mergeTo(myGroup);
-                connections.add(conn);
-                conn.changeOwner(this);
+                add(conn);
+                if (conn.getState() == Call.State.HOLDING) {
+                    conn.unhold(audioGroup);
+                }
             }
-            that.connections.clear();
             that.setState(Call.State.IDLE);
         }
 
+        private void add(SipConnection conn) {
+            SipCall call = conn.getCall();
+            if (call == this) return;
+            if (call != null) call.connections.remove(conn);
+
+            connections.add(conn);
+            conn.changeOwner(this);
+        }
+
         void sendDtmf(char c) {
             AudioGroup audioGroup = getAudioGroup();
             if (audioGroup == null) return;
@@ -571,7 +593,6 @@
     private class SipConnection extends SipConnectionBase {
         private SipCall mOwner;
         private SipAudioCall mSipAudioCall;
-        private AudioGroup mOriginalGroup;
         private Call.State mState = Call.State.IDLE;
         private SipProfile mPeer;
         private boolean mIncoming = false;
@@ -676,6 +697,7 @@
         }
 
         void hold() throws CallStateException {
+            setState(Call.State.HOLDING);
             try {
                 mSipAudioCall.holdCall();
             } catch (SipException e) {
@@ -683,7 +705,9 @@
             }
         }
 
-        void unhold() throws CallStateException {
+        void unhold(AudioGroup audioGroup) throws CallStateException {
+            mSipAudioCall.setAudioGroup(audioGroup);
+            setState(Call.State.ACTIVE);
             try {
                 mSipAudioCall.continueCall();
             } catch (SipException e) {
@@ -691,16 +715,6 @@
             }
         }
 
-        void mergeTo(AudioGroup group) throws CallStateException {
-            AudioStream stream = mSipAudioCall.getAudioStream();
-            if (stream == null) {
-                throw new CallStateException("wrong state to merge: "
-                        + mSipAudioCall.getState());
-            }
-            if (mOriginalGroup == null) mOriginalGroup = getAudioGroup();
-            stream.join(group);
-        }
-
         @Override
         protected void setState(Call.State state) {
             if (state == mState) return;
@@ -735,29 +749,36 @@
 
         @Override
         public void hangup() throws CallStateException {
-            // TODO: need to pull AudioStream out of the AudioGroup in case
-            // this conn was part of a conf call
-            Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": "
-                    + ": on phone " + getPhone());
-            try {
-                mSipAudioCall.endCall();
-                setState(Call.State.DISCONNECTING);
-                setDisconnectCause(DisconnectCause.LOCAL);
-            } catch (SipException e) {
-                throw new CallStateException("hangup(): " + e);
+            synchronized (SipPhone.class) {
+                Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": "
+                        + ": on phone " + getPhone().getPhoneName());
+                try {
+                    mSipAudioCall.endCall();
+                    setState(Call.State.DISCONNECTING);
+                    setDisconnectCause(DisconnectCause.LOCAL);
+                } catch (SipException e) {
+                    throw new CallStateException("hangup(): " + e);
+                }
             }
         }
 
         @Override
         public void separate() throws CallStateException {
-            // TODO: what's this for SIP?
-            /*
-            if (!disconnected) {
-                owner.separate(this);
-            } else {
-                throw new CallStateException ("disconnected");
+            synchronized (SipPhone.class) {
+                SipCall call = (SipCall) SipPhone.this.getBackgroundCall();
+                if (call.getState() != Call.State.IDLE) {
+                    throw new CallStateException(
+                            "cannot put conn back to a call in non-idle state: "
+                            + call.getState());
+                }
+                Log.v(LOG_TAG, "separate conn: " + mPeer.getUriString()
+                        + " from " + mOwner + " back to " + call);
+
+                AudioGroup audioGroup = call.getAudioGroup(); // may be null
+                call.add(this);
+                mSipAudioCall.setAudioGroup(audioGroup);
+                call.hold();
             }
-            */
         }
 
     }
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
index 721b8af..1d33be9 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -174,10 +174,6 @@
         return state;
     }
 
-    public String getPhoneName() {
-        return "SIP";
-    }
-
     public int getPhoneType() {
         // FIXME: add SIP phone type
         return Phone.PHONE_TYPE_GSM;
diff --git a/tests/CoreTests/android/core/JavaPerformanceTests.java b/tests/CoreTests/android/core/JavaPerformanceTests.java
index fbe70cc..95075ea 100644
--- a/tests/CoreTests/android/core/JavaPerformanceTests.java
+++ b/tests/CoreTests/android/core/JavaPerformanceTests.java
@@ -23,7 +23,6 @@
 
     public static String[] children() {
         return new String[] {
-                StringTest.class.getName(),
                 HashMapPerformanceTest.class.getName(),
                 ArrayListPerformanceTest.class.getName(),
                 TreeMapPerformanceTest.class.getName(),
diff --git a/tests/CoreTests/android/core/StringTest.java b/tests/CoreTests/android/core/StringTest.java
deleted file mode 100644
index 128531c..0000000
--- a/tests/CoreTests/android/core/StringTest.java
+++ /dev/null
@@ -1,951 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.core;
-
-import java.util.Locale;
-
-import android.test.PerformanceTestBase;
-import android.test.PerformanceTestCase;
-
-public class StringTest extends PerformanceTestBase {
-    public static final int ITERATIONS = 1000;
-    public static final String STATIC_STRING_01 = "Hello Android";
-    public static final String STATIC_STRING_02 =
-            "Remember, today is the tomorrow you worried about yesterday";
-    public static final char[] STATIC_CHAR_ARRAY =
-            {'N', 'A', 'N', 'D', 'R', 'O', 'I', 'D'};
-    public static StringBuffer STATIC_SBUF = new StringBuffer(STATIC_STRING_02);
-
-    @Override
-    public int startPerformance(PerformanceTestCase.Intermediates intermediates) {
-        intermediates.setInternalIterations(ITERATIONS);
-        return 0;
-    }
-
-    /** Create an empty String object* */
-
-    public void testStringCreate() {
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-        }
-    }
-
-    /** Create an initialised String object* */
-
-    public void testStringCreate1() {
-        String rString, str = STATIC_STRING_01;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str); // 10
-        }
-    }
-
-    /** equals() with for loop* */
-    public void testStringEquals() {
-        String mString = new String(STATIC_STRING_01);
-        String str = STATIC_STRING_01;
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-        }
-    }
-
-    /**
-     * ContentEquals- Comparing the content of a String with that of a String
-     * Buffer*
-     */
-
-    public void testStringContentEquals() {
-        StringBuffer sBuf = new StringBuffer(STATIC_STRING_01);
-        String str = STATIC_STRING_01;
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-        }
-    }
-
-    /** Compare string objects lexicographically using compareTo() with for loop* */
-
-    public void testStringCompareTo() {
-        String str1 = new String(STATIC_STRING_01);
-        String str2 = STATIC_STRING_01;
-        int result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-        }
-
-    }
-
-    /** Compare string objects using compareToIgnorecase() with for loop* */
-
-    public void testStringCompareToIgnoreCase() {
-        String mString = new String(STATIC_STRING_01);
-        String str2 = STATIC_STRING_01;
-        int result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-        }
-    }
-
-    /** startsWith * */
-
-    public void testStringstartsWith() {
-        boolean result;
-        String str1 = STATIC_STRING_02, str2 = "Rem";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-        }
-    }
-
-    /** startsWith(String seq, int begin) * */
-
-    public void testStringstartsWith1() {
-        String str1 = STATIC_STRING_02, str2 = "tom";
-        int pos = 10;
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-        }
-    }
-
-    /** endsWith * */
-
-    public void testStringendsWith() {
-        String str = STATIC_STRING_02, str1 = "day";
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-        }
-    }
-
-    /**
-     * indexOf to determine whether a string contains a substring
-     */
-    public void testStringindexOf() {
-        boolean result;
-        String str = STATIC_STRING_02, str1 = "tomo";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-        }
-    }
-
-    /** indexOf()* */
-
-    public void testStringindexOf1() {
-        int index;
-        String str = STATIC_STRING_02;
-        char c = 't';
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-        }
-
-    }
-
-    /** indexOf(char c, int start)* */
-    public void testStringindexOf2() {
-        int index, pos = 12;
-        String str = STATIC_STRING_02, str1 = "tom";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-        }
-    }
-
-    /** lastIndexOf()* */
-
-    public void testStringlastIndexOf() {
-        int index;
-        char c = 't';
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-        }
-    }
-
-    /** lastIndexOf()* */
-
-    public void testStringlastIndexOf1() {
-        int index, pos = 36;
-        String str = STATIC_STRING_02, str1 = "tom";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-        }
-    }
-
-    /**
-     * contains() to determine whether a string contains a substring
-     */
-
-    public void testStringcontains() {
-        boolean result;
-        String str = STATIC_STRING_02, str1 = "tomo";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-        }
-    }
-
-    /** substring(int start) */
-
-    public void testStringsubstring() {
-        String rString;
-        String str = STATIC_STRING_02;
-        int index = 10;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-        }
-    }
-
-    /** substring(int start, int end) in a for loop* */
-
-    public void testStringsubstring1() {
-        String rString;
-        String str = STATIC_STRING_02;
-        int start = 10, end = 48;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-        }
-    }
-
-    /**
-     * valueOf(char[] cArray) String representation of a character array
-     */
-    public void testStringvalueOf() {
-        String rString;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-        }
-    }
-
-    /** valueOf(char[] cArray, int offset, int count)* */
-
-    public void testStringvalueOf1() {
-        String rString;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        int start = 1, end = 7;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-        }
-    }
-
-    /** Convert a string to a char Array* */
-
-    public void testStringtoCharArray() {
-        char[] cArray;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-        }
-    }
-
-    /** length()* */
-
-    public void testStringlength() {
-        int len;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-        }
-    }
-
-    /** hashcode()* */
-
-    public void testStringhashCode() {
-        int index;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-        }
-    }
-
-    /** replace()* */
-
-    public void testStringreplace() {
-        String rString;
-        String str = STATIC_STRING_02;
-        char c1 = ' ', c2 = ' ';
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-        }
-    }
-
-    public void testStringreplaceAll() {
-        String rString;
-        String str = STATIC_STRING_02, str1 = " ", str2 = "/";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-        }
-    }
-
-    /** Convert a StringBuffer to a String* */
-
-    public void testStringtoString() {
-        StringBuffer sBuf = new StringBuffer(STATIC_STRING_02);
-
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-        }
-    }
-
-    /** Split a string into an array of strings* */
-
-    public void testStringsplit() {
-        String[] strings;
-        String str1 = STATIC_STRING_02, str = " ";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-
-        }
-    }
-
-    /** Split a string into an array of strings* */
-
-    public void testStringsplit1() {
-        String str = STATIC_STRING_02, str1 = " ";
-        String[] strings;
-        int pos = 8;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-        }
-    }
-
-    public void testStringgetBytes() {
-        byte[] bytes;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-        }
-    }
-
-    /** copyValueOf(char[] data) * */
-
-    public void testStringcopyValueOf() {
-        String rString;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-        }
-    }
-
-    /** copyValueOf(char[] data, int index, int count)* */
-
-    public void testStringcopyValueOf1() {
-        String rString;
-        int start = 1, end = 7;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-        }
-    }
-
-    /** trim()* */
-
-    public void testStringtrim() {
-        String mString =
-                new String(
-                        "                            HELLO ANDROID                                                ");
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-        }
-    }
-
-    /** getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)* */
-
-    public void testStringgetChars() {
-        char[] cArray = STATIC_CHAR_ARRAY;
-        String str = STATIC_STRING_01;
-        int value1 = 7, value2 = 12, value3 = 1;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-        }
-    }
-
-    /** toUpperCase()* */
-
-    public void testStringtoUpperCase() {
-        String rString, str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-        }
-    }
-
-    /** toUpperCase() with locale* */
-
-    public void testStringtoUpperCase1() {
-        Locale locale = new Locale("tr");
-        String str = STATIC_STRING_02;
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-        }
-    }
-
-    /** toLowerCase* */
-
-    public void StringtoLowerCase() {
-        String rString, str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-        }
-    }
-
-    /** toLowerCase with locale* */
-
-    public void testStringtoLowerCase1() {
-        Locale locale = new Locale("tr");
-        String rString, str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-        }
-    }
-
-    /** charAt()* */
-
-    public void testStringcharAt() {
-        String str = STATIC_STRING_02;
-        int index, pos = 21;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-        }
-    }
-
-    public void testStringConcat() {
-        String mString, str1 = STATIC_STRING_01, str2 = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-        }
-    }
-
-    public void testStringBufferAppend() {
-        StringBuffer sBuf = new StringBuffer(" ");
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-        }
-    }
-
-    public void testStringBufferInsert() {
-        StringBuffer sBuf = new StringBuffer(" ");
-        int index = sBuf.length();
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-        }
-    }
-
-    public void testStringBufferReverse() {
-        StringBuffer sBuf = STATIC_SBUF;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-        }
-    }
-
-    public void testStringBufferSubstring() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String rString;
-        int index = 0;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-        }
-    }
-
-    public void testStringBufferSubstring1() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String rString;
-        int start = 5, end = 25;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-        }
-    }
-
-    public void testStringBufferReplace() {
-        StringBuffer sBuf = STATIC_SBUF;
-        int start = 3, end = 6;
-        String str = "ind";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-        }
-    }
-
-    public void testStringBufferIndexOf() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String str = "t";
-        int index;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-        }
-    }
-
-    public void testStringBufferIndexOf1() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String str = "tom";
-        int index, pos = 12;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-        }
-
-    }
-
-    public void testStringBufferLastIndexOf() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String str = "t";
-        int index;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-        }
-    }
-
-    public void testStringBufferLastIndexOf1() {
-        StringBuffer sBuf = STATIC_SBUF;
-        int index, pos = 36;
-        String str = "tom";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-        }
-    }
-}
diff --git a/tests/DumpRenderTree/AndroidManifest.xml b/tests/DumpRenderTree/AndroidManifest.xml
index 03b7e26..c151251 100644
--- a/tests/DumpRenderTree/AndroidManifest.xml
+++ b/tests/DumpRenderTree/AndroidManifest.xml
@@ -18,15 +18,18 @@
     <application android:name="HTMLHostApp">
         <uses-library android:name="android.test.runner" />
         <activity android:name="Menu" android:label="Dump Render Tree"
-          android:screenOrientation="portrait">
+          android:screenOrientation="portrait"
+          android:theme="@android:style/Theme.Light">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
         <activity android:name="TestShellActivity" android:launchMode="singleTop"
-          android:screenOrientation="portrait"/>
-        <activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait"/>
+          android:screenOrientation="portrait"
+          android:theme="@android:style/Theme.Light"/>
+        <activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait"
+          android:theme="@android:style/Theme.Light"/>
     </application>
 
     <instrumentation android:name=".LayoutTestsAutoRunner"
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 9f580a3..4cff3de2 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -95,6 +95,7 @@
         ignoreResultList.add("http/tests/appcache/fallback.html"); // http://b/issue?id=2713004
         ignoreResultList.add("http/tests/appcache/foreign-iframe-main.html"); // flaky - skips states
         ignoreResultList.add("http/tests/appcache/manifest-with-empty-file.html"); // flaky
+        ignoreResultList.add("http/tests/appcache/origin-quota.html"); // needs clearAllApplicationCaches(), see http://b/issue?id=2944196
         ignoreResultList.add("storage/database-lock-after-reload.html"); // Succeeds but DumpRenderTree does not read result correctly
         ignoreResultList.add("storage/hash-change-with-xhr.html"); // Succeeds but DumpRenderTree does not read result correctly
         ignoreResultList.add("storage/open-database-creation-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
index 0c2347d..716086b 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
@@ -16,14 +16,12 @@
 
 package com.android.dumprendertree;
 
-import android.os.Handler;
 import android.os.SystemClock;
 import android.util.*;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.webkit.WebView;
 
-import java.lang.InterruptedException;
 import java.util.Arrays;
 import java.util.Vector;
 
@@ -33,7 +31,7 @@
 	
     WebViewEventSender(WebView webView) {
         mWebView = webView;
-        mTouchPoints = new Vector();
+        mTouchPoints = new Vector<TouchPoint>();
     }
 	
 	public void resetMouse() {
@@ -82,47 +80,23 @@
 		mouseUp();
 	}
 
-	public void mouseDown() {
-          /*  KeyEvent event = new KeyEvent(
-                KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);
-            mWebView.onKeyDown(event.getKeyCode(), event); */
-	}
+    public void mouseDown() {
+        long ts = SystemClock.uptimeMillis();
+        MotionEvent event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_DOWN, mouseX, mouseY, 0);
+        mWebView.onTouchEvent(event);
+    }
 
-	public void mouseMoveTo(int X, int Y) {
-		if (X > mouseX) {
-                    KeyEvent event = new KeyEvent(
-                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
-                    mWebView.onKeyDown(event.getKeyCode(), event);
-                    mWebView.onKeyUp(event.getKeyCode(), event);
-		} else if ( X < mouseX ) {
-                    KeyEvent event = new KeyEvent(
-                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
-                    mWebView.onKeyDown(event.getKeyCode(), event);
-                    mWebView.onKeyUp(event.getKeyCode(), event);
-		}
-		if (Y > mouseY) {
-                    KeyEvent event = new KeyEvent(
-                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
-                    mWebView.onKeyDown(event.getKeyCode(), event);
-                    mWebView.onKeyUp(event.getKeyCode(), event);
-		} else if (Y < mouseY ) {
-                    KeyEvent event = new KeyEvent(
-                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
-                    mWebView.onKeyDown(event.getKeyCode(), event);
-                    mWebView.onKeyUp(event.getKeyCode(), event);
-		}
-		mouseX= X;
-		mouseY= Y;
-	
-	}
+    public void mouseMoveTo(int X, int Y) {
+        mouseX= X;
+        mouseY= Y;
+    }
 
-	public void mouseUp() {
-        /*    KeyEvent event = new KeyEvent(
-                KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER);
-            mWebView.onKeyDown(event.getKeyCode(), event);*/
+     public void mouseUp() {
+        long ts = SystemClock.uptimeMillis();
+        MotionEvent event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_UP, mouseX, mouseY, 0);
+        mWebView.onTouchEvent(event);
+    }
 
-	}
-	
 	// Assumes lowercase chars, case needs to be
 	// handled by calling function.
 	static int keyMapper(char c) {
@@ -365,7 +339,7 @@
             mX = x;
             mY = y;
         }
-    };
+    }
 
     private Vector<TouchPoint> mTouchPoints;
     private int mTouchMetaState;
diff --git a/tests/DumpRenderTree2/assets/run_apache2.py b/tests/DumpRenderTree2/assets/run_apache2.py
index f404090..5764f0e 100755
--- a/tests/DumpRenderTree2/assets/run_apache2.py
+++ b/tests/DumpRenderTree2/assets/run_apache2.py
@@ -33,7 +33,7 @@
   else:
     run_cmd = sys.argv[1]
 
-  #Setup logging class
+  # Setup logging class
   logging.basicConfig(level=logging.INFO, format='%(message)s')
 
   if not run_cmd in ("start", "stop", "restart"):
@@ -41,55 +41,69 @@
     logging.info("Usage: python run_apache2.py start|stop|restart")
     return
 
-  #Create /tmp/WebKit if it doesn't exist. This is needed for various files used by apache2
+  # Create /tmp/WebKit if it doesn't exist. This is needed for various files used by apache2
   tmp_WebKit = os.path.join("/tmp", "WebKit")
   if not os.path.exists(tmp_WebKit):
     os.mkdir(tmp_WebKit)
 
-  #Get the path to android tree root based on the script location.
-  #Basically we go 5 levels up
+  # Get the path to android tree root based on the script location.
+  # Basically we go 5 levels up
   parent = os.pardir
   script_location = os.path.abspath(os.path.dirname(sys.argv[0]))
   android_tree_root = os.path.join(script_location, parent, parent, parent, parent, parent)
   android_tree_root = os.path.normpath(android_tree_root)
 
-  #Prepare the command to set ${APACHE_RUN_USER} and ${APACHE_RUN_GROUP}
+  # Paths relative to android_tree_root
+  webkit_path = os.path.join("external", "webkit")
+  layout_tests_path = os.path.join(webkit_path, "LayoutTests")
+  http_conf_path = os.path.join(layout_tests_path, "http", "conf")
+
+  # Prepare the command to set ${APACHE_RUN_USER} and ${APACHE_RUN_GROUP}
   envvars_path = os.path.join("/etc", "apache2", "envvars")
   export_envvars_cmd = "source " + envvars_path
 
   error_log_path = os.path.join(tmp_WebKit, "apache2-error.log")
+  custom_log_path = os.path.join(tmp_WebKit, "apache2-access.log")
 
-  #Prepare the command to (re)start/stop the server with specified settings
+  # Prepare the command to (re)start/stop the server with specified settings
   apache2_restart_cmd = "apache2 -k " + run_cmd
   directives  = " -c \"ServerRoot " + android_tree_root + "\""
-  directives += " -c \"DocumentRoot " + os.path.join("external", "webkit") + "\""
 
-  #This directive is commented out in apache2-debian-httpd.conf for some reason
-  #However, it is useful to browse through tests in the browser, so it's added here.
-  #One thing to note is that because of problems with mod_dir and port numbers, mod_dir
-  #is turned off. That means that there _must_ be a trailing slash at the end of URL
-  #for auto indexes to work correctly.
+  # We use http/tests as the document root as the HTTP tests use hardcoded
+  # resources at the server root. We then use aliases to make available the
+  # complete set of tests and the required scripts.
+  directives += " -c \"DocumentRoot " + os.path.join(layout_tests_path, "http", "tests/") + "\""
+  directives += " -c \"Alias /LayoutTests " + layout_tests_path + "\""
+  directives += " -c \"Alias /WebKitTools/DumpRenderTree/android " + \
+    os.path.join(webkit_path, "WebKitTools", "DumpRenderTree", "android") + "\""
+
+  # This directive is commented out in apache2-debian-httpd.conf for some reason
+  # However, it is useful to browse through tests in the browser, so it's added here.
+  # One thing to note is that because of problems with mod_dir and port numbers, mod_dir
+  # is turned off. That means that there _must_ be a trailing slash at the end of URL
+  # for auto indexes to work correctly.
   directives += " -c \"LoadModule autoindex_module /usr/lib/apache2/modules/mod_autoindex.so\""
 
   directives += " -c \"ErrorLog " + error_log_path +"\""
-  directives += " -c \"SSLCertificateFile " + os.path.join ("external", "webkit", "LayoutTests",
-    "http", "conf", "webkit-httpd.pem") + "\""
+  directives += " -c \"CustomLog " + custom_log_path + " combined\""
+  directives += " -c \"SSLCertificateFile " + os.path.join(http_conf_path, "webkit-httpd.pem") + \
+    "\""
   directives += " -c \"User ${APACHE_RUN_USER}\""
   directives += " -c \"Group ${APACHE_RUN_GROUP}\""
   directives += " -C \"TypesConfig " + os.path.join("/etc", "mime.types") + "\""
-  conf_file_cmd = " -f " + os.path.join(android_tree_root, "external", "webkit", "LayoutTests",
-    "http", "conf", "apache2-debian-httpd.conf")
+  conf_file_cmd = " -f " + \
+    os.path.join(android_tree_root, http_conf_path, "apache2-debian-httpd.conf")
 
-  #Try to execute the commands
+  # Try to execute the commands
   logging.info("Will " + run_cmd + " apache2 server.")
   cmd = export_envvars_cmd + " && " + apache2_restart_cmd + directives + conf_file_cmd
   p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   (out, err) = p.communicate()
 
-  #Output the stdout from the command to console
+  # Output the stdout from the command to console
   logging.info(out)
 
-  #Report any errors
+  # Report any errors
   if p.returncode != 0:
     logging.info("!! ERRORS:")
 
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java
index 8fca629..bb9a916 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java
@@ -63,7 +63,8 @@
                 port = url.getPort();
             }
         } catch (MalformedURLException e) {
-            Log.e(LOG_TAG + "::appendDatabaseCallback", e.getMessage());
+            Log.e(LOG_TAG, "urlString=" + urlString + " databaseIdentifier=" + databaseIdentifier,
+                    e);
         }
 
         output.append("UI DELEGATE DATABASE CALLBACK: ");
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
index cf82d24..bcc8564 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
@@ -124,9 +124,10 @@
                 }
             }
         } catch (FileNotFoundException e) {
-            Log.w(LOG_TAG + "::reloadConfiguration", "File not found: " + txt_exp.getPath());
+            Log.w(LOG_TAG, "mRootDirPath=" + mRootDirPath + ": File not found: " +
+                    txt_exp.getPath(), e);
         } catch (IOException e) {
-            Log.e(LOG_TAG + "::reloadConfiguration", "IOException: " + e.getMessage());
+            Log.e(LOG_TAG, "mRootDirPath=" + mRootDirPath, e);
         }
     }
 
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
index 212c187..0cfe69d 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
@@ -31,13 +31,13 @@
     public static final String LOG_TAG = "FsUtils";
 
     public static void writeDataToStorage(File file, byte[] bytes, boolean append) {
-        Log.d(LOG_TAG + "::writeDataToStorage", file.getAbsolutePath());
+        Log.d(LOG_TAG, "writeDataToStorage(): " + file.getAbsolutePath());
         try {
             OutputStream outputStream = null;
             try {
                 file.getParentFile().mkdirs();
                 file.createNewFile();
-                Log.d(LOG_TAG + "::writeDataToStorage", "File created.");
+                Log.d(LOG_TAG, "writeDataToStorage(): File created.");
                 outputStream = new FileOutputStream(file, append);
                 outputStream.write(bytes);
             } finally {
@@ -46,13 +46,13 @@
                 }
             }
         } catch (IOException e) {
-            Log.e(LOG_TAG + "::writeDataToStorage", e.getMessage());
+            Log.e(LOG_TAG, "file.getAbsolutePath=" + file.getAbsolutePath(), e);
         }
     }
 
     public static byte[] readDataFromStorage(File file) {
         if (!file.exists()) {
-            Log.d(LOG_TAG + "::readDataFromStorage", "File does not exist: "
+            Log.d(LOG_TAG, "readDataFromStorage(): File does not exist: "
                     + file.getAbsolutePath());
             return null;
         }
@@ -70,7 +70,7 @@
                 }
             }
         } catch (IOException e) {
-            Log.e(LOG_TAG + "::readDataFromStorage", e.getMessage());
+            Log.e(LOG_TAG, "file.getAbsolutePath=" + file.getAbsolutePath(), e);
         }
 
         return bytes;
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
index 6db9571..7d57eb7 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
@@ -56,7 +56,7 @@
     }
 
     public void clearAllDatabases() {
-        Log.w(LOG_TAG + "::clearAllDatabases", "called");
+        Log.i(LOG_TAG, "clearAllDatabases() called");
         WebStorage.getInstance().deleteAllData();
     }
 
@@ -70,7 +70,7 @@
 
     public void setDatabaseQuota(long quota) {
         /** TODO: Reset this before every test! */
-        Log.w(LOG_TAG + "::setDatabaseQuota", "called with: " + quota);
+        Log.i(LOG_TAG, "setDatabaseQuota() called with: " + quota);
         WebStorage.getInstance().setQuotaForOrigin(Uri.fromFile(new File("")).toString(),
                 quota);
     }
@@ -80,13 +80,13 @@
     }
 
     public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
-        Log.w(LOG_TAG + "::setMockGeolocationPosition", "latitude: " + latitude +
-                " longitude: " + longitude + " accuracy: " + accuracy);
+        Log.i(LOG_TAG, "setMockGeolocationPosition(): " + "latitude=" + latitude +
+                " longitude=" + longitude + " accuracy=" + accuracy);
         MockGeolocation.getInstance().setPosition(latitude, longitude, accuracy);
     }
 
     public void setMockGeolocationError(int code, String message) {
-        Log.w(LOG_TAG + "::setMockGeolocationError", "code: " + code + " message: " + message);
+        Log.i(LOG_TAG, "setMockGeolocationError(): " + "code=" + code + " message=" + message);
         MockGeolocation.getInstance().setError(code, message);
     }
 
@@ -94,7 +94,10 @@
             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
         // Configuration is in WebKit, so stay on WebCore thread, but go via LayoutTestsExecutor
         // as we need access to the Webview.
+        Log.i(LOG_TAG, "setMockDeviceOrientation(" + canProvideAlpha +
+                ", " + alpha + ", " + canProvideBeta + ", " + beta + ", " + canProvideGamma +
+                ", " + gamma + ")");
         mLayoutTestsExecutor.setMockDeviceOrientation(
                 canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
     }
-}
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index 4737657..d17ec7c 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -112,7 +112,7 @@
     private boolean mDumpDatabaseCallbacks;
     private boolean mIsGeolocationPermissionSet;
     private boolean mGeolocationPermission;
-    private Map mPendingGeolocationPermissionCallbacks;
+    private Map<GeolocationPermissions.Callback, String> mPendingGeolocationPermissionCallbacks;
 
     private EventSender mEventSender = new EventSender();
 
@@ -338,7 +338,7 @@
             serviceMsg.setData(bundle);
             mManagerServiceMessenger.send(serviceMsg);
         } catch (RemoteException e) {
-            Log.e(LOG_TAG + "::startTests", e.getMessage());
+            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
         }
 
         runNextTest();
@@ -369,6 +369,7 @@
     private void onTestTimedOut() {
         assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
 
+        Log.w(LOG_TAG, "onTestTimedOut(): " + mCurrentTestRelativePath);
         mCurrentTestTimedOut = true;
 
         /**
@@ -383,6 +384,7 @@
     private void onTestFinished() {
         assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
 
+        Log.i(LOG_TAG, "onTestFinished(): " + mCurrentTestRelativePath);
         obtainActualResultsFromWebView();
     }
 
@@ -406,6 +408,7 @@
         assert mCurrentState == CurrentState.OBTAINING_RESULT
                 : "mCurrentState = " + mCurrentState.name();
 
+        Log.i(LOG_TAG, "onActualResultsObtained(): " + mCurrentTestRelativePath);
         mCurrentState = CurrentState.IDLE;
 
         mResultHandler.removeMessages(MSG_TEST_TIMED_OUT);
@@ -436,7 +439,7 @@
             serviceMsg.setData(bundle);
             mManagerServiceMessenger.send(serviceMsg);
         } catch (RemoteException e) {
-            Log.e(LOG_TAG + "::reportResultToService", e.getMessage());
+            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
         }
     }
 
@@ -455,7 +458,7 @@
                     Message.obtain(null, ManagerService.MSG_ALL_TESTS_FINISHED);
             mManagerServiceMessenger.send(serviceMsg);
         } catch (RemoteException e) {
-            Log.e(LOG_TAG + "::onAllTestsFinished", e.getMessage());
+            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
         }
 
         unbindService(mServiceConnection);
@@ -529,11 +532,11 @@
                     mGeolocationPermission = msg.arg1 == 1;
 
                     if (mPendingGeolocationPermissionCallbacks != null) {
-                        Iterator iter = mPendingGeolocationPermissionCallbacks.keySet().iterator();
+                        Iterator<GeolocationPermissions.Callback> iter =
+                                mPendingGeolocationPermissionCallbacks.keySet().iterator();
                         while (iter.hasNext()) {
-                            GeolocationPermissions.Callback callback =
-                                    (GeolocationPermissions.Callback) iter.next();
-                            String origin = (String) mPendingGeolocationPermissionCallbacks.get(callback);
+                            GeolocationPermissions.Callback callback = iter.next();
+                            String origin = mPendingGeolocationPermissionCallbacks.get(callback);
                             callback.invoke(origin, mGeolocationPermission, false);
                         }
                         mPendingGeolocationPermissionCallbacks = null;
@@ -541,7 +544,7 @@
                     break;
 
                 default:
-                    Log.w(LOG_TAG + "::handleMessage", "Message code does not exist: " + msg.what);
+                    assert false : "msg.what=" + msg.what;
                     break;
             }
         }
@@ -555,41 +558,42 @@
     }
 
     public void waitUntilDone() {
-        Log.w(LOG_TAG + "::waitUntilDone", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
     }
 
     public void notifyDone() {
-        Log.w(LOG_TAG + "::notifyDone", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
     }
 
     public void dumpAsText(boolean enablePixelTest) {
-        Log.w(LOG_TAG + "::dumpAsText(" + enablePixelTest + ")", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpAsText(" + enablePixelTest + ") called");
         /** TODO: Implement */
         if (enablePixelTest) {
-            Log.w(LOG_TAG + "::dumpAsText", "enablePixelTest not implemented, switching to false");
+            Log.w(LOG_TAG, "enablePixelTest not implemented, switching to false");
         }
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_AS_TEXT);
     }
 
     public void dumpChildFramesAsText() {
-        Log.w(LOG_TAG + "::dumpChildFramesAsText", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpChildFramesAsText() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_CHILD_FRAMES_AS_TEXT);
     }
 
     public void setCanOpenWindows() {
-        Log.w(LOG_TAG + "::setCanOpenWindows", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
     }
 
     public void dumpDatabaseCallbacks() {
-        Log.w(LOG_TAG + "::dumpDatabaseCallbacks:", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpDatabaseCallbacks() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_DATABASE_CALLBACKS);
     }
 
     public void setGeolocationPermission(boolean allow) {
-        Log.w(LOG_TAG + "::setGeolocationPermission", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
+                ") called");
         Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
         msg.arg1 = allow ? 1 : 0;
         msg.sendToTarget();
@@ -597,7 +601,10 @@
 
     public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setMockDeviceOrientation(" + canProvideAlpha +
+                ", " + alpha + ", " + canProvideBeta + ", " + beta + ", " + canProvideGamma +
+                ", " + gamma + ")");
         mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
                 canProvideGamma, gamma);
     }
-}
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
index 31026d6..951126f 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -88,7 +88,7 @@
                     break;
 
                 case MSG_PROCESS_ACTUAL_RESULTS:
-                    Log.d(LOG_TAG + ".mIncomingHandler", msg.getData().getString("relativePath"));
+                    Log.d(LOG_TAG,"mIncomingHandler: " + msg.getData().getString("relativePath"));
                     onActualResultsObtained(msg.getData());
                     break;
 
@@ -151,11 +151,13 @@
         AbstractResult results =
                 AbstractResult.TestType.valueOf(bundle.getString("type")).createResult(bundle);
 
+        Log.i(LOG_TAG, "onActualResultObtained: " + results.getRelativePath());
         handleResults(results);
     }
 
     private void ensureNextTestSetup(String nextTest, int index) {
         if (nextTest == null) {
+            Log.w(LOG_TAG, "ensureNextTestSetup(): nextTest=null");
             return;
         }
 
@@ -172,8 +174,8 @@
     private void onTestCrashed() {
         handleResults(new CrashedDummyResult(mCurrentlyRunningTest));
 
-        Log.w(LOG_TAG + "::onTestCrashed", mCurrentlyRunningTest +
-                "(" + mCurrentlyRunningTestIndex + ")");
+        Log.w(LOG_TAG, "onTestCrashed(): " + mCurrentlyRunningTest +
+                " (" + mCurrentlyRunningTestIndex + ")");
 
         Intent intent = new Intent(this, TestsListActivity.class);
         intent.setAction(Intent.ACTION_REBOOT);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
index 250b6bc..26bf75c 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
@@ -204,4 +204,4 @@
         }
         return html.toString();
     }
-}
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java
new file mode 100644
index 0000000..d165a1a
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * The utility class that can setup a socket allowing the device to communicate with remote
+ * machines through the machine that the device is connected to via adb.
+ */
+public class AdbUtils {
+    private static final String LOG_TAG = "AdbUtils";
+
+    private static final String ADB_OK = "OKAY";
+    private static final int ADB_PORT = 5037;
+    private static final String ADB_HOST = "127.0.0.1";
+    private static final int ADB_RESPONSE_SIZE = 4;
+
+    /**
+     * Send an ADB command using existing socket connection
+     *
+     * The streams provided must be from a socket connected to adb already
+     *
+     * @param is input stream of the socket connection
+     * @param os output stream of the socket
+     * @param cmd the adb command to send
+     * @return if adb gave a success response
+     * @throws IOException
+     */
+    private static boolean sendAdbCmd(InputStream is, OutputStream os, String cmd)
+            throws IOException {
+        byte[] buf = new byte[ADB_RESPONSE_SIZE];
+
+        cmd = String.format("%04X", cmd.length()) + cmd;
+        os.write(cmd.getBytes());
+        int read = is.read(buf);
+        if (read != ADB_RESPONSE_SIZE || !ADB_OK.equals(new String(buf))) {
+            Log.w(LOG_TAG, "adb cmd faild.");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Get a tcp socket connection to specified IP address and port proxied by adb
+     *
+     * The proxying is transparent, e.g. if a socket is returned, then it can be written to and
+     * read from as if it is directly connected to the target
+     *
+     * @param remoteAddress IP address of the host to connect to
+     * @param remotePort port of the host to connect to
+     * @return a valid Socket instance if successful, null otherwise
+     */
+    public static Socket getSocketToRemoteMachine(String remoteAddress, int remotePort) {
+        try {
+            Socket socket = new Socket(ADB_HOST, ADB_PORT);
+            String cmd = "tcp:" + remotePort + ":" + remoteAddress;
+            if (!sendAdbCmd(socket.getInputStream(), socket.getOutputStream(), cmd)) {
+                socket.close();
+                return null;
+            }
+            return socket;
+        } catch (IOException ioe) {
+            Log.w(LOG_TAG, "error creating adb socket", ioe);
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
new file mode 100644
index 0000000..6a6cb7f
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * Worker class for {@link Forwarder}. A ConnectionHandler will be created once the Forwarder
+ * accepts an incoming connection, and it will then forward the incoming/outgoing streams to a
+ * connection already proxied by adb networking (see also {@link AdbUtils}).
+ */
+public class ConnectionHandler {
+
+    private static final String LOG_TAG = "ConnectionHandler";
+
+    private class SocketPipeThread extends Thread {
+
+        private Socket mInSocket, mOutSocket;
+
+        public SocketPipeThread(Socket inSocket, Socket outSocket) {
+            mInSocket = inSocket;
+            mOutSocket = outSocket;
+        }
+
+        @Override
+        public void run() {
+            InputStream is;
+            OutputStream os;
+            try {
+                synchronized (this) {
+                    is = mInSocket.getInputStream();
+                    os = mOutSocket.getOutputStream();
+                }
+            } catch (IOException e) {
+                Log.w(LOG_TAG, this.toString(), e);
+                return;
+            }
+
+            byte[] buffer = new byte[4096];
+            int length;
+            while (true) {
+                try {
+                    synchronized (this) {
+                        if ((length = is.read(buffer)) <= 0) {
+                            break;
+                        }
+                        os.write(buffer, 0, length);
+                    }
+                } catch (IOException e) {
+                    /** This exception means one of the streams is closed */
+                    Log.v(LOG_TAG, this.toString(), e);
+                    break;
+                }
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "SocketPipeThread:\n" + mInSocket + "\n=>\n" + mOutSocket;
+        }
+    }
+
+    private Socket mFromSocket, mToSocket;
+    private SocketPipeThread mFromToPipe, mToFromPipe;
+
+    public ConnectionHandler(Socket fromSocket, Socket toSocket) {
+        mFromSocket = fromSocket;
+        mToSocket = toSocket;
+        mFromToPipe = new SocketPipeThread(mFromSocket, mToSocket);
+        mToFromPipe = new SocketPipeThread(mToSocket, mFromSocket);
+    }
+
+    public void start() {
+        mFromToPipe.start();
+        mToFromPipe.start();
+    }
+
+    public void stop() {
+        shutdown(mFromSocket);
+        shutdown(mToSocket);
+    }
+
+    private void shutdown(Socket socket) {
+        try {
+            synchronized (mFromToPipe) {
+                synchronized (mToFromPipe) {
+                    /** This will stop the while loop in the run method */
+                    socket.shutdownInput();
+                    socket.shutdownOutput();
+                    socket.close();
+                }
+            }
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
new file mode 100644
index 0000000..e5ef6da
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A port forwarding server. Listens on localhost on specified port and forwards the tcp
+ * communications to external socket via adb networking proxy.
+ */
+public class Forwarder extends Thread {
+    private static final String LOG_TAG = "Forwarder";
+
+    private int mPort;
+    private String mRemoteMachineIpAddress;
+
+    private Boolean mIsRunning = false;
+    private ServerSocket mServerSocket;
+
+    private Set<ConnectionHandler> mConnectionHandlers = new HashSet<ConnectionHandler>();
+
+    public Forwarder(int port, String remoteMachineIpAddress) {
+        mPort = port;
+        mRemoteMachineIpAddress = remoteMachineIpAddress;
+    }
+
+    @Override
+    public void start() {
+        Log.i(LOG_TAG, "start(): Starting fowarder on port: " + mPort);
+        synchronized (this) {
+            if (mIsRunning) {
+                Log.w(LOG_TAG, "start(): Forwarder on port: " + mPort + " already running! NOOP.");
+                return;
+            }
+        }
+
+        try {
+            mServerSocket = new ServerSocket(mPort);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "mPort=" + mPort, e);
+            return;
+        }
+
+        mIsRunning = true;
+        super.start();
+    }
+
+    @Override
+    public void run() {
+        while (true) {
+            synchronized (this) {
+                if (!mIsRunning) {
+                    return;
+                }
+
+                /** These sockets will be closed when Forwarder.stop() is called */
+                Socket localSocket;
+                Socket remoteSocket;
+                try {
+                    localSocket = mServerSocket.accept();
+                    remoteSocket = AdbUtils.getSocketToRemoteMachine(mRemoteMachineIpAddress,
+                            mPort);
+                } catch (IOException e) {
+                    /** This most likely means that mServerSocket is already closed */
+                    Log.w(LOG_TAG + "mPort=" + mPort, e);
+                    return;
+                }
+
+                if (remoteSocket == null) {
+                    try {
+                        localSocket.close();
+                    } catch (IOException e) {
+                        Log.e(LOG_TAG, "mPort=" + mPort, e);
+                    }
+
+                    Log.e(LOG_TAG, "run(): mPort= " + mPort + " Failed to start forwarding from " +
+                            localSocket);
+                    continue;
+                }
+
+                ConnectionHandler forwarder = new ConnectionHandler(localSocket, remoteSocket);
+                mConnectionHandlers.add(forwarder);
+                forwarder.start();
+
+            }
+        }
+    }
+
+    public void finish() {
+        synchronized (this) {
+            if (!mIsRunning) {
+                return;
+            }
+        }
+
+        try {
+            mServerSocket.close();
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "mPort=" + mPort, e);
+        }
+
+        synchronized (this) {
+            mIsRunning = false;
+        }
+
+        for (ConnectionHandler connectionHandler : mConnectionHandlers) {
+            connectionHandler.stop();
+        }
+        mConnectionHandlers.clear();
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java
new file mode 100644
index 0000000..10fee4b
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A simple class to start and stop Forwarders running on some ports.
+ *
+ * It uses a singleton pattern and is thread safe.
+ */
+public class ForwarderManager {
+    /**
+     * The IP address of the server serving the tests.
+     */
+    private static final String HOST_IP = "127.0.0.1";
+
+    /**
+     * We use these ports because other webkit platforms do. They are set up in
+     * external/webkit/LayoutTests/http/conf/apache2-debian-httpd.conf
+     */
+    public static final int HTTP_PORT = 8080;
+    public static final int HTTPS_PORT = 8443;
+
+    private static ForwarderManager forwarderManager;
+
+    private Set<Forwarder> mServers;
+
+    private ForwarderManager() {
+        mServers = new HashSet<Forwarder>(2);
+        mServers.add(new Forwarder(HTTP_PORT, HOST_IP));
+        mServers.add(new Forwarder(HTTPS_PORT, HOST_IP));
+    }
+
+    public static synchronized ForwarderManager getForwarderManager() {
+        if (forwarderManager == null) {
+            forwarderManager = new ForwarderManager();
+        }
+        return forwarderManager;
+    }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+    public synchronized void start() {
+        for (Forwarder server : mServers) {
+            server.start();
+        }
+    }
+
+    public synchronized void stop() {
+        for (Forwarder server : mServers) {
+            server.finish();
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
index ddfae69..e730c8a 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
@@ -66,7 +66,7 @@
                 try {
                     this.wait();
                 } catch (InterruptedException e) {
-                    Log.e(LOG_TAG + "::startLayoutTests", e.getMessage());
+                    Log.e(LOG_TAG, "startLayoutTests()", e);
                 }
             }
         }
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java
index c638958..94b936b 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java
@@ -21,6 +21,7 @@
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.util.DisplayMetrics;
+import android.view.ContextMenu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
@@ -83,6 +84,17 @@
 
         ListView list = (ListView) findViewById(R.id.list);
         list.setAdapter(adapter);
+        
+        registerForContextMenu(list);
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+        super.onCreateContextMenu(menu, v, menuInfo);
+        menu.setHeaderTitle("Context menu");
+        menu.add("List item 1");
+        menu.add("List item 2");
+        menu.add("List item 3");
     }
 
     private static class SimpleListAdapter extends ArrayAdapter<String> {
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
index f645446..abe9d5e 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
@@ -36,6 +36,7 @@
         private final Paint mMediumPaint;
         private final Paint mLargePaint;
         private final Paint mStrikePaint;
+        private final Paint mScaledPaint;
 
         CustomTextView(Context c) {
             super(c);
@@ -43,14 +44,19 @@
             mMediumPaint = new Paint();
             mMediumPaint.setAntiAlias(true);
             mMediumPaint.setColor(0xffff0000);
+
             mLargePaint = new Paint();
             mLargePaint.setAntiAlias(true);
             mLargePaint.setTextSize(36.0f);
+
             mStrikePaint = new Paint();
             mStrikePaint.setAntiAlias(true);
             mStrikePaint.setTextSize(16.0f);
             mStrikePaint.setUnderlineText(true);
-            
+
+            mScaledPaint = new Paint();
+            mScaledPaint.setAntiAlias(true);
+            mScaledPaint.setTextSize(16.0f);
         }
 
         @Override
@@ -86,6 +92,13 @@
             mStrikePaint.setStrikeThruText(false);
             mStrikePaint.setUnderlineText(true);
             
+            mScaledPaint.setTextScaleX(0.5f);
+            canvas.drawText("Hello OpenGL renderer!", 500, 200, mScaledPaint);
+            mScaledPaint.setTextScaleX(2.0f);
+            canvas.drawText("Hello OpenGL renderer!", 500, 230, mScaledPaint);
+            mScaledPaint.setTextScaleX(1.0f);
+            canvas.drawText("Hello OpenGL renderer!", 500, 260, mScaledPaint);
+            
             canvas.save();
             canvas.clipRect(150.0f, 220.0f, 450.0f, 320.0f);
             canvas.drawText("Hello OpenGL renderer!", 100, 300, mLargePaint);
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index c40af80..b50a393 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -141,9 +141,9 @@
     if (bundle->getVerbose()) {
         printf("Archive:  %s\n", zipFileName);
         printf(
-            " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n");
+            " Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name\n");
         printf(
-            "--------  ------  ------- -----   ----   ----   ------    ----\n");
+            "--------  ------  ------- -----  -------      ----  ----  ------    ----\n");
     }
 
     totalUncLen = totalCompLen = 0;
@@ -159,12 +159,13 @@
             strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
                 localtime(&when));
 
-            printf("%8ld  %-7.7s %7ld %3d%%  %s  %08lx  %s\n",
+            printf("%8ld  %-7.7s %7ld %3d%%  %8zd  %s  %08lx  %s\n",
                 (long) entry->getUncompressedLen(),
                 compressionName(entry->getCompressionMethod()),
                 (long) entry->getCompressedLen(),
                 calcPercent(entry->getUncompressedLen(),
                             entry->getCompressedLen()),
+                (size_t) entry->getLFHOffset(),
                 dateBuf,
                 entry->getCRC32(),
                 entry->getFileName());
@@ -338,6 +339,7 @@
     SMALL_SCREEN_ATTR = 0x01010284,
     NORMAL_SCREEN_ATTR = 0x01010285,
     LARGE_SCREEN_ATTR = 0x01010286,
+    XLARGE_SCREEN_ATTR = 0x010102bf,
     REQUIRED_ATTR = 0x0101028e,
 };
 
@@ -572,6 +574,7 @@
             int smallScreen = 1;
             int normalScreen = 1;
             int largeScreen = 1;
+            int xlargeScreen = 1;
             String8 pkg;
             String8 activityName;
             String8 activityLabel;
@@ -754,6 +757,8 @@
                                 NORMAL_SCREEN_ATTR, NULL, 1);
                         largeScreen = getIntegerAttribute(tree,
                                 LARGE_SCREEN_ATTR, NULL, 1);
+                        xlargeScreen = getIntegerAttribute(tree,
+                                XLARGE_SCREEN_ATTR, NULL, 1);
                     } else if (tag == "uses-feature") {
                         String8 name = getAttribute(tree, NAME_ATTR, &error);
 
@@ -1082,10 +1087,15 @@
             if (largeScreen > 0) {
                 largeScreen = targetSdk >= 4 ? -1 : 0;
             }
+            if (xlargeScreen > 0) {
+                // Introduced in Honeycomb.
+                xlargeScreen = targetSdk >= 10 ? -1 : 0;
+            }
             printf("supports-screens:");
             if (smallScreen != 0) printf(" 'small'");
             if (normalScreen != 0) printf(" 'normal'");
             if (largeScreen != 0) printf(" 'large'");
+            if (xlargeScreen != 0) printf(" 'xlarge'");
             printf("\n");
 
             printf("locales:");
diff --git a/tools/aapt/ZipEntry.h b/tools/aapt/ZipEntry.h
index 7f721b4..c2f3227 100644
--- a/tools/aapt/ZipEntry.h
+++ b/tools/aapt/ZipEntry.h
@@ -72,6 +72,11 @@
     off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
 
     /*
+     * Return the offset of the local file header.
+     */
+    off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
+
+    /*
      * Return the absolute file offset of the start of the compressed or
      * uncompressed data.
      */
@@ -186,11 +191,6 @@
     void setModWhen(time_t when);
 
     /*
-     * Return the offset of the local file header.
-     */
-    off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
-
-    /*
      * Set the offset of the local file header, relative to the start of
      * the current file.
      */
diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp
index 2a9bf04..49e077f 100644
--- a/tools/obbtool/Main.cpp
+++ b/tools/obbtool/Main.cpp
@@ -29,7 +29,7 @@
 static int wantUsage = 0;
 static int wantVersion = 0;
 
-#define ADD_OPTS "n:v:f:c:"
+#define ADD_OPTS "n:v:o"
 static const struct option longopts[] = {
     {"help",       no_argument, &wantUsage,   1},
     {"version",    no_argument, &wantVersion, 1},
@@ -37,8 +37,7 @@
     /* Args for "add" */
     {"name",       required_argument, NULL, 'n'},
     {"version",    required_argument, NULL, 'v'},
-    {"filesystem", required_argument, NULL, 'f'},
-    {"crypto",     required_argument, NULL, 'c'},
+    {"overlay",    optional_argument, NULL, 'o'},
 
     {NULL, 0, NULL, '\0'}
 };
@@ -46,8 +45,7 @@
 struct package_info_t {
     char* packageName;
     int packageVersion;
-    char* filesystem;
-    char* crypto;
+    bool overlay;
 };
 
 /*
@@ -77,6 +75,7 @@
 
     obb->setPackageName(String8(info->packageName));
     obb->setVersion(info->packageVersion);
+    obb->setOverlay(info->overlay);
 
     if (!obb->writeTo(filename)) {
         fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n",
@@ -112,6 +111,8 @@
     printf("OBB info for '%s':\n", filename);
     printf("Package name: %s\n", obb->getPackageName().string());
     printf("     Version: %d\n", obb->getVersion());
+    printf("       Flags: 0x%08x\n", obb->getFlags());
+    printf("     Overlay: %s\n", obb->isOverlay() ? "true" : "false");
 }
 
 /*
@@ -143,7 +144,7 @@
         case 'n':
             package_info.packageName = optarg;
             break;
-        case 'v':
+        case 'v': {
             char *end;
             package_info.packageVersion = strtol(optarg, &end, 10);
             if (*optarg == '\0' || *end != '\0') {
@@ -152,11 +153,9 @@
                 goto bail;
             }
             break;
-        case 'f':
-            package_info.filesystem = optarg;
-            break;
-        case 'c':
-            package_info.crypto = optarg;
+        }
+        case 'o':
+            package_info.overlay = true;
             break;
         case '?':
             wantUsage = 1;
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
index dc86082..37cc121 100644
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ b/voip/java/android/net/rtp/AudioGroup.java
@@ -49,27 +49,28 @@
     synchronized void add(AudioStream stream, AudioCodec codec, int codecType, int dtmfType) {
         if (!mStreams.containsKey(stream)) {
             try {
-                int id = add(stream.getMode(), stream.dup(),
+                int socket = stream.dup();
+                add(stream.getMode(), socket,
                         stream.getRemoteAddress().getHostAddress(), stream.getRemotePort(),
                         codec.name, codec.sampleRate, codec.sampleCount, codecType, dtmfType);
-                mStreams.put(stream, id);
+                mStreams.put(stream, socket);
             } catch (NullPointerException e) {
                 throw new IllegalStateException(e);
             }
         }
     }
 
-    private native int add(int mode, int socket, String remoteAddress, int remotePort,
+    private native void add(int mode, int socket, String remoteAddress, int remotePort,
             String codecName, int sampleRate, int sampleCount, int codecType, int dtmfType);
 
     synchronized void remove(AudioStream stream) {
-        Integer id = mStreams.remove(stream);
-        if (id != null) {
-            remove(id);
+        Integer socket = mStreams.remove(stream);
+        if (socket != null) {
+            remove(socket);
         }
     }
 
-    private native void remove(int id);
+    private native void remove(int socket);
 
     /**
      * Sends a DTMF digit to every {@link AudioStream} in this group. Currently
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index f4be839..3cdd114 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -244,7 +244,8 @@
      * Also, the {@code AudioStream} may change its group during a call (e.g.,
      * after the call is held/un-held). Finally, the {@code AudioGroup} object
      * returned by this method is undefined after the call ends or the
-     * {@link #close} method is called.
+     * {@link #close} method is called. If a group object is set by
+     * {@link #setAudioGroup(AudioGroup)}, then this method returns that object.
      *
      * @return the {@link AudioGroup} object or null if the RTP stream has not
      *      yet been set up
@@ -253,6 +254,15 @@
     AudioGroup getAudioGroup();
 
     /**
+     * Sets the {@link AudioGroup} object which the {@link AudioStream} object
+     * joins. If {@code audioGroup} is null, then the {@code AudioGroup} object
+     * will be dynamically created when needed.
+     *
+     * @see #getAudioStream
+     */
+    void setAudioGroup(AudioGroup audioGroup);
+
+    /**
      * Checks if the call is established.
      *
      * @return true if the call is established
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 7161309..b8ac6d7 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -70,7 +70,8 @@
     private ISipSession mSipSession;
     private SdpSessionDescription mPeerSd;
 
-    private AudioStream mRtpSession;
+    private AudioStream mAudioStream;
+    private AudioGroup mAudioGroup;
     private SdpSessionDescription.AudioCodec mCodec;
     private long mSessionId = -1L; // SDP session ID
     private boolean mInCall = false;
@@ -505,11 +506,19 @@
     }
 
     public synchronized AudioStream getAudioStream() {
-        return mRtpSession;
+        return mAudioStream;
     }
 
     public synchronized AudioGroup getAudioGroup() {
-        return ((mRtpSession == null) ? null : mRtpSession.getAudioGroup());
+        if (mAudioGroup != null) return mAudioGroup;
+        return ((mAudioStream == null) ? null : mAudioStream.getAudioGroup());
+    }
+
+    public synchronized void setAudioGroup(AudioGroup group) {
+        if ((mAudioStream != null) && (mAudioStream.getAudioGroup() != null)) {
+            mAudioStream.join(group);
+        }
+        mAudioGroup = group;
     }
 
     private SdpSessionDescription.AudioCodec getCodec(SdpSessionDescription sd) {
@@ -561,7 +570,7 @@
             // TODO: get sample rate from sdp
             mCodec = getCodec(peerSd);
 
-            AudioStream audioStream = mRtpSession;
+            AudioStream audioStream = mAudioStream;
             audioStream.associate(InetAddress.getByName(peerMediaAddress),
                     peerMediaPort);
             audioStream.setCodec(convert(mCodec), mCodec.payloadType);
@@ -580,7 +589,7 @@
                     Log.d(TAG, "   not sending");
                     audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY);
                 }
-            } else {
+
                 /* The recorder volume will be very low if the device is in
                  * IN_CALL mode. Therefore, we have to set the mode to NORMAL
                  * in order to have the normal microphone level.
@@ -590,14 +599,22 @@
                         .setMode(AudioManager.MODE_NORMAL);
             }
 
-            AudioGroup audioGroup = new AudioGroup();
-            audioStream.join(audioGroup);
+            // AudioGroup logic:
+            AudioGroup audioGroup = getAudioGroup();
             if (mHold) {
-                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
-            } else if (mMuted) {
-                audioGroup.setMode(AudioGroup.MODE_MUTED);
+                if (audioGroup != null) {
+                    audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+                }
+                // don't create an AudioGroup here; doing so will fail if
+                // there's another AudioGroup out there that's active
             } else {
-                audioGroup.setMode(AudioGroup.MODE_NORMAL);
+                if (audioGroup == null) audioGroup = new AudioGroup();
+                audioStream.join(audioGroup);
+                if (mMuted) {
+                    audioGroup.setMode(AudioGroup.MODE_MUTED);
+                } else {
+                    audioGroup.setMode(AudioGroup.MODE_NORMAL);
+                }
             }
         } catch (Exception e) {
             Log.e(TAG, "call()", e);
@@ -606,20 +623,20 @@
 
     private void stopCall(boolean releaseSocket) {
         Log.d(TAG, "stop audiocall");
-        if (mRtpSession != null) {
-            mRtpSession.join(null);
+        if (mAudioStream != null) {
+            mAudioStream.join(null);
 
             if (releaseSocket) {
-                mRtpSession.release();
-                mRtpSession = null;
+                mAudioStream.release();
+                mAudioStream = null;
             }
         }
     }
 
     private int getLocalMediaPort() {
-        if (mRtpSession != null) return mRtpSession.getLocalPort();
+        if (mAudioStream != null) return mAudioStream.getLocalPort();
         try {
-            AudioStream s = mRtpSession =
+            AudioStream s = mAudioStream =
                     new AudioStream(InetAddress.getByName(getLocalIp()));
             return s.getLocalPort();
         } catch (IOException e) {
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index e71c293..6c99141 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -35,7 +35,7 @@
  * Class containing a SIP account, domain and server information.
  * @hide
  */
-public class SipProfile implements Parcelable, Serializable {
+public class SipProfile implements Parcelable, Serializable, Cloneable {
     private static final long serialVersionUID = 1L;
     private static final int DEFAULT_PORT = 5060;
     private Address mAddress;
@@ -46,6 +46,8 @@
     private String mProfileName;
     private boolean mSendKeepAlive = false;
     private boolean mAutoRegistration = true;
+    private boolean mAllowOutgoingCall = false;
+    private int mCallingUid = -1;
 
     /** @hide */
     public static final Parcelable.Creator<SipProfile> CREATOR =
@@ -79,6 +81,23 @@
         }
 
         /**
+         * Creates a builder based on the given profile.
+         */
+        public Builder(SipProfile profile) {
+            if (profile == null) throw new NullPointerException();
+            try {
+                mProfile = (SipProfile) profile.clone();
+            } catch (CloneNotSupportedException e) {
+                throw new RuntimeException("should not occur", e);
+            }
+            mProfile.mAddress = null;
+            mUri = profile.getUri();
+            mUri.setUserPassword(profile.getPassword());
+            mDisplayName = profile.getDisplayName();
+            mProxyAddress = profile.getProxyAddress();
+        }
+
+        /**
          * Constructor.
          *
          * @param uriString the URI string as "sip:<user_name>@<domain>"
@@ -226,6 +245,18 @@
         }
 
         /**
+         * Sets the allow-outgoing-call flag.
+         *
+         * @param flag true if allowing to make outgoing call on the profile;
+         *      false otherwise
+         * @return this builder object
+         */
+        public Builder setOutgoingCallAllowed(boolean flag) {
+            mProfile.mAllowOutgoingCall = flag;
+            return this;
+        }
+
+        /**
          * Builds and returns the SIP profile object.
          *
          * @return the profile object created
@@ -262,6 +293,8 @@
         mProfileName = in.readString();
         mSendKeepAlive = (in.readInt() == 0) ? false : true;
         mAutoRegistration = (in.readInt() == 0) ? false : true;
+        mAllowOutgoingCall = (in.readInt() == 0) ? false : true;
+        mCallingUid = in.readInt();
     }
 
     /** @hide */
@@ -274,6 +307,8 @@
         out.writeString(mProfileName);
         out.writeInt(mSendKeepAlive ? 1 : 0);
         out.writeInt(mAutoRegistration ? 1 : 0);
+        out.writeInt(mAllowOutgoingCall ? 1 : 0);
+        out.writeInt(mCallingUid);
     }
 
     /** @hide */
@@ -398,4 +433,27 @@
     public boolean getAutoRegistration() {
         return mAutoRegistration;
     }
+
+    /**
+     * Returns true if allowing to make outgoing calls on this profile.
+     */
+    public boolean isOutgoingCallAllowed() {
+        return mAllowOutgoingCall;
+    }
+
+    /**
+     * Sets the calling process's Uid in the sip service.
+     * @hide
+     */
+    public void setCallingUid(int uid) {
+        mCallingUid = uid;
+    }
+
+    /**
+     * Gets the calling process's Uid in the sip settings.
+     * @hide
+     */
+    public int getCallingUid() {
+        return mCallingUid;
+    }
 }
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index fc1ed9b..bb45a9a 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -367,7 +367,7 @@
             MSG_TRUNC | MSG_DONTWAIT);
 
         // Do we need to check SSRC, sequence, and timestamp? They are not
-        // reliable but at least they can be used to identity duplicates?
+        // reliable but at least they can be used to identify duplicates?
         if (length < 12 || length > (int)sizeof(buffer) ||
             (ntohl(*(uint32_t *)buffer) & 0xC07F0000) != mCodecMagic) {
             LOGD("stream[%d] malformed packet", mSocket);
@@ -526,70 +526,6 @@
     LOGD("group[%d] is dead", mDeviceSocket);
 }
 
-#define FROYO_COMPATIBLE
-#ifdef FROYO_COMPATIBLE
-
-// Copied from AudioRecord.cpp.
-status_t AudioRecord_getMinFrameCount(
-        int* frameCount,
-        uint32_t sampleRate,
-        int format,
-        int channelCount)
-{
-    size_t size = 0;
-    if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &size)
-            != NO_ERROR) {
-        LOGE("AudioSystem could not query the input buffer size.");
-        return NO_INIT;
-    }
-
-    if (size == 0) {
-        LOGE("Unsupported configuration: sampleRate %d, format %d, channelCount %d",
-            sampleRate, format, channelCount);
-        return BAD_VALUE;
-    }
-
-    // We double the size of input buffer for ping pong use of record buffer.
-    size <<= 1;
-
-    if (AudioSystem::isLinearPCM(format)) {
-        size /= channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1);
-    }
-
-    *frameCount = size;
-    return NO_ERROR;
-}
-
-// Copied from AudioTrack.cpp.
-status_t AudioTrack_getMinFrameCount(
-        int* frameCount,
-        int streamType,
-        uint32_t sampleRate)
-{
-    int afSampleRate;
-    if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
-        return NO_INIT;
-    }
-    int afFrameCount;
-    if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
-        return NO_INIT;
-    }
-    uint32_t afLatency;
-    if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
-        return NO_INIT;
-    }
-
-    // Ensure that buffer depth covers at least audio hardware latency
-    uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
-    if (minBufCount < 2) minBufCount = 2;
-
-    *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount :
-              afFrameCount * minBufCount * sampleRate / afSampleRate;
-    return NO_ERROR;
-}
-
-#endif
-
 bool AudioGroup::set(int sampleRate, int sampleCount)
 {
     mEventQueue = epoll_create(2);
@@ -603,15 +539,6 @@
     // Find out the frame count for AudioTrack and AudioRecord.
     int output = 0;
     int input = 0;
-#ifdef FROYO_COMPATIBLE
-    if (AudioTrack_getMinFrameCount(&output, AudioSystem::VOICE_CALL,
-        sampleRate) != NO_ERROR || output <= 0 ||
-        AudioRecord_getMinFrameCount(&input, sampleRate,
-        AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) {
-        LOGE("cannot compute frame count");
-        return false;
-    }
-#else
     if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL,
         sampleRate) != NO_ERROR || output <= 0 ||
         AudioRecord::getMinFrameCount(&input, sampleRate,
@@ -619,13 +546,13 @@
         LOGE("cannot compute frame count");
         return false;
     }
-#endif
     LOGD("reported frame count: output %d, input %d", output, input);
 
-    output = (output + sampleCount - 1) / sampleCount * sampleCount;
-    input = (input + sampleCount - 1) / sampleCount * sampleCount;
-    if (input < output * 2) {
-        input = output * 2;
+    if (output < sampleCount * 2) {
+        output = sampleCount * 2;
+    }
+    if (input < sampleCount * 2) {
+        input = sampleCount * 2;
     }
     LOGD("adjusted frame count: output %d, input %d", output, input);
 
@@ -661,7 +588,7 @@
     // Give device socket a reasonable timeout and buffer size.
     timeval tv;
     tv.tv_sec = 0;
-    tv.tv_usec = 1000 * sampleCount / sampleRate * 500;
+    tv.tv_usec = 1000 * sampleCount / sampleRate * 1000;
     if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) ||
         setsockopt(pair[0], SOL_SOCKET, SO_RCVBUF, &output, sizeof(output)) ||
         setsockopt(pair[1], SOL_SOCKET, SO_SNDBUF, &output, sizeof(output))) {
@@ -771,6 +698,10 @@
     for (AudioStream *stream = mChain; stream->mNext; stream = stream->mNext) {
         AudioStream *target = stream->mNext;
         if (target->mSocket == socket) {
+            if (epoll_ctl(mEventQueue, EPOLL_CTL_DEL, socket, NULL)) {
+                LOGE("epoll_ctl: %s", strerror(errno));
+                return false;
+            }
             stream->mNext = target->mNext;
             LOGD("stream[%d] leaves group[%d]", socket, mDeviceSocket);
             delete target;
@@ -834,38 +765,62 @@
     if (recv(mDeviceSocket, output, sizeof(output), 0) <= 0) {
         memset(output, 0, sizeof(output));
     }
-    if (mTrack.write(output, sizeof(output)) != (int)sizeof(output)) {
-        LOGE("cannot write to AudioTrack");
+
+    int16_t input[mSampleCount];
+    int toWrite = mSampleCount;
+    int toRead = (mMode == MUTED) ? 0 : mSampleCount;
+    int chances = 100;
+
+    while (--chances > 0 && (toWrite > 0 || toRead > 0)) {
+        if (toWrite > 0) {
+            AudioTrack::Buffer buffer;
+            buffer.frameCount = toWrite;
+
+            status_t status = mTrack.obtainBuffer(&buffer, 1);
+            if (status == NO_ERROR) {
+                memcpy(buffer.i8, &output[mSampleCount - toWrite], buffer.size);
+                toWrite -= buffer.frameCount;
+                mTrack.releaseBuffer(&buffer);
+            } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
+                LOGE("cannot write to AudioTrack");
+                return false;
+            }
+        }
+
+        if (toRead > 0) {
+            AudioRecord::Buffer buffer;
+            buffer.frameCount = mRecord.frameCount();
+
+            status_t status = mRecord.obtainBuffer(&buffer, 1);
+            if (status == NO_ERROR) {
+                int count = (buffer.frameCount < toRead) ?
+                        buffer.frameCount : toRead;
+                memcpy(&input[mSampleCount - toRead], buffer.i8, count * 2);
+                toRead -= count;
+                if (buffer.frameCount < mRecord.frameCount()) {
+                    buffer.frameCount = count;
+                }
+                mRecord.releaseBuffer(&buffer);
+            } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
+                LOGE("cannot read from AudioRecord");
+                return false;
+            }
+        }
+    }
+
+    if (!chances) {
+        LOGE("device loop timeout");
         return false;
     }
 
     if (mMode != MUTED) {
-        uint32_t frameCount = mRecord.frameCount();
-        AudioRecord::Buffer input;
-        input.frameCount = frameCount;
-
-        if (mRecord.obtainBuffer(&input, -1) != NO_ERROR) {
-            LOGE("cannot read from AudioRecord");
-            return false;
-        }
-
-        if (input.frameCount < (uint32_t)mSampleCount) {
-            input.frameCount = 0;
+        if (mMode == NORMAL) {
+            send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT);
         } else {
-            if (mMode == NORMAL) {
-                send(mDeviceSocket, input.i8, sizeof(output), MSG_DONTWAIT);
-            } else {
-                // TODO: Echo canceller runs here.
-                send(mDeviceSocket, input.i8, sizeof(output), MSG_DONTWAIT);
-            }
-            if (input.frameCount < frameCount) {
-                input.frameCount = mSampleCount;
-            }
+            // TODO: Echo canceller runs here.
+            send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT);
         }
-
-        mRecord.releaseBuffer(&input);
     }
-
     return true;
 }
 
@@ -874,7 +829,7 @@
 static jfieldID gNative;
 static jfieldID gMode;
 
-jint add(JNIEnv *env, jobject thiz, jint mode,
+void add(JNIEnv *env, jobject thiz, jint mode,
     jint socket, jstring jRemoteAddress, jint remotePort,
     jstring jCodecName, jint sampleRate, jint sampleCount,
     jint codecType, jint dtmfType)
@@ -887,7 +842,7 @@
     sockaddr_storage remote;
     if (parse(env, jRemoteAddress, remotePort, &remote) < 0) {
         // Exception already thrown.
-        return -1;
+        goto error;
     }
     if (sampleRate < 0 || sampleCount < 0 || codecType < 0 || codecType > 127) {
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
@@ -895,12 +850,12 @@
     }
     if (!jCodecName) {
         jniThrowNullPointerException(env, "codecName");
-        return -1;
+        goto error;
     }
     codecName = env->GetStringUTFChars(jCodecName, NULL);
     if (!codecName) {
         // Exception already thrown.
-        return -1;
+        goto error;
     }
 
     // Create audio stream.
@@ -909,8 +864,10 @@
         codecType, dtmfType)) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "cannot initialize audio stream");
+        env->ReleaseStringUTFChars(jCodecName, codecName);
         goto error;
     }
+    env->ReleaseStringUTFChars(jCodecName, codecName);
     socket = -1;
 
     // Create audio group.
@@ -934,16 +891,13 @@
 
     // Succeed.
     env->SetIntField(thiz, gNative, (int)group);
-    env->ReleaseStringUTFChars(jCodecName, codecName);
-    return socket;
+    return;
 
 error:
     delete group;
     delete stream;
     close(socket);
     env->SetIntField(thiz, gNative, NULL);
-    env->ReleaseStringUTFChars(jCodecName, codecName);
-    return -1;
 }
 
 void remove(JNIEnv *env, jobject thiz, jint socket)
@@ -976,7 +930,7 @@
 }
 
 JNINativeMethod gMethods[] = {
-    {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)I", (void *)add},
+    {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)V", (void *)add},
     {"remove", "(I)V", (void *)remove},
     {"setMode", "(I)V", (void *)setMode},
     {"sendDtmf", "(I)V", (void *)sendDtmf},
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 01bc919..ca4f29f 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -216,7 +216,7 @@
     /**
      * The network's SSID. Can either be an ASCII string,
      * which must be enclosed in double quotation marks
-     * (e.g., {@code &quot;MyNetwork&quot;}, or a string of
+     * (e.g., {@code "MyNetwork"}, or a string of
      * hex digits,which are not enclosed in quotes
      * (e.g., {@code 01a243f405}).
      */
@@ -239,7 +239,7 @@
     public String preSharedKey;
     /**
      * Up to four WEP keys. Either an ASCII string enclosed in double
-     * quotation marks (e.g., {@code &quot;abcdef&quot;} or a string
+     * quotation marks (e.g., {@code "abcdef"} or a string
      * of hex digits (e.g., {@code 0102030405}).
      * <p/>
      * When the value of one of these keys is read, the actual key is
@@ -432,6 +432,38 @@
         return 0;
     }
 
+    /**
+     * Returns a copy of this WifiConfiguration.
+     *
+     * @return a copy of this WifiConfiguration.
+     * @hide
+     */
+    public WifiConfiguration clone() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = networkId;
+        config.status = status;
+        config.SSID = SSID;
+        config.BSSID = BSSID;
+        config.preSharedKey = preSharedKey;
+
+        for (int i = 0; i < wepKeys.length; i++)
+            config.wepKeys[i] = wepKeys[i];
+
+        config.wepTxKeyIndex = wepTxKeyIndex;
+        config.priority = priority;
+        config.hiddenSSID = hiddenSSID;
+        config.allowedKeyManagement   = (BitSet) allowedKeyManagement.clone();
+        config.allowedProtocols       = (BitSet) allowedProtocols.clone();
+        config.allowedAuthAlgorithms  = (BitSet) allowedAuthAlgorithms.clone();
+        config.allowedPairwiseCiphers = (BitSet) allowedPairwiseCiphers.clone();
+        config.allowedGroupCiphers    = (BitSet) allowedGroupCiphers.clone();
+
+        for (int i = 0; i < enterpriseFields.length; i++) {
+            config.enterpriseFields[i].setValue(enterpriseFields[i].value());
+        }
+        return config;
+    }
+
     /** Implement the Parcelable interface {@hide} */
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(networkId);
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 19c4eb0..013dcb6 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -79,6 +79,7 @@
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.BitSet;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -126,6 +127,7 @@
     private int mReconnectCount = 0;
     private boolean mIsScanMode = false;
     private boolean mConfigChanged = false;
+    private List<WifiConfiguration> mConfiguredNetworks = new ArrayList<WifiConfiguration>();
 
     /**
      * Instance of the bluetooth headset helper. This needs to be created
@@ -232,12 +234,8 @@
     private static final int CMD_BLACKLIST_NETWORK                = 56;
     /* Clear the blacklist network list */
     private static final int CMD_CLEAR_BLACKLIST                  = 57;
-    /* Get the configured networks */
-    private static final int CMD_GET_NETWORK_CONFIG               = 58;
     /* Save configuration */
-    private static final int CMD_SAVE_CONFIG                      = 59;
-    /* Connection status */
-    private static final int CMD_CONNECTION_STATUS                = 60;
+    private static final int CMD_SAVE_CONFIG                      = 58;
 
     /* Supplicant commands after driver start*/
     /* Initiate a scan */
@@ -379,10 +377,6 @@
     /* Soft Ap is running */
     private HierarchicalState mSoftApStartedState = new SoftApStartedState();
 
-    /* Argument for Message object to indicate a synchronous call */
-    private static final int SYNCHRONOUS_CALL = 1;
-    private static final int ASYNCHRONOUS_CALL = 0;
-
 
     /**
      * One of  {@link WifiManager#WIFI_STATE_DISABLED},
@@ -488,7 +482,7 @@
     /**
      * TODO: doc
      */
-    public boolean pingSupplicant() {
+    public boolean syncPingSupplicant() {
         return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
     }
 
@@ -535,14 +529,14 @@
     /**
      * TODO: doc
      */
-    public int getWifiState() {
+    public int syncGetWifiState() {
         return mWifiState.get();
     }
 
     /**
      * TODO: doc
      */
-    public String getWifiStateByName() {
+    public String syncGetWifiStateByName() {
         switch (mWifiState.get()) {
             case WIFI_STATE_DISABLING:
                 return "disabling";
@@ -562,14 +556,14 @@
     /**
      * TODO: doc
      */
-    public int getWifiApState() {
+    public int syncGetWifiApState() {
         return mWifiApState.get();
     }
 
     /**
      * TODO: doc
      */
-    public String getWifiApStateByName() {
+    public String syncGetWifiApStateByName() {
         switch (mWifiApState.get()) {
             case WIFI_AP_STATE_DISABLING:
                 return "disabling";
@@ -591,11 +585,11 @@
      * @return a {@link WifiInfo} object containing information about the current connection
      *
      */
-    public WifiInfo requestConnectionInfo() {
+    public WifiInfo syncRequestConnectionInfo() {
         return mWifiInfo;
     }
 
-    public DhcpInfo getDhcpInfo() {
+    public DhcpInfo syncGetDhcpInfo() {
         return mDhcpInfo;
     }
 
@@ -635,7 +629,7 @@
     /**
      * TODO: doc
      */
-    public List<ScanResult> getScanResultsList() {
+    public List<ScanResult> syncGetScanResultsList() {
         return mScanResults;
     }
 
@@ -665,12 +659,19 @@
      *
      * @return network id of the new network
      */
-    public int addOrUpdateNetwork(WifiConfiguration config) {
+    public int syncAddOrUpdateNetwork(WifiConfiguration config) {
         return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
     }
 
-    public List<WifiConfiguration> getConfiguredNetworks() {
-        return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList;
+    public List<WifiConfiguration> syncGetConfiguredNetworks() {
+        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+        synchronized (mConfiguredNetworks) {
+            Iterator<WifiConfiguration> iterator = mConfiguredNetworks.iterator();
+            while(iterator.hasNext()) {
+                networks.add(iterator.next().clone());
+            }
+        }
+        return networks;
     }
 
     /**
@@ -678,7 +679,7 @@
      *
      * @param networkId id of the network to be removed
      */
-    public boolean removeNetwork(int networkId) {
+    public boolean syncRemoveNetwork(int networkId) {
         return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
     }
 
@@ -697,7 +698,7 @@
      * @param disableOthers true, if all other networks have to be disabled
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
-    public boolean enableNetwork(int netId, boolean disableOthers) {
+    public boolean syncEnableNetwork(int netId, boolean disableOthers) {
         return sendSyncMessage(CMD_ENABLE_NETWORK,
                 new EnableNetParams(netId, disableOthers)).boolValue;
     }
@@ -708,7 +709,7 @@
      * @param netId network id of the network
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
-    public boolean disableNetwork(int netId) {
+    public boolean syncDisableNetwork(int netId) {
         return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
     }
 
@@ -735,7 +736,11 @@
     }
 
     public void connectNetwork(WifiConfiguration wifiConfig) {
-        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, wifiConfig));
+        /* arg1 is used to indicate netId, force a netId value of -1 when
+         * we are passing a configuration since the default value of
+         * 0 is a valid netId
+         */
+        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig));
     }
 
     public void saveNetwork(WifiConfiguration wifiConfig) {
@@ -746,23 +751,6 @@
         sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
     }
 
-    /**
-     * Get detailed status of the connection
-     *
-     * @return Example status result
-     *  bssid=aa:bb:cc:dd:ee:ff
-     *  ssid=TestNet
-     *  id=3
-     *  pairwise_cipher=NONE
-     *  group_cipher=NONE
-     *  key_mgmt=NONE
-     *  wpa_state=COMPLETED
-     *  ip_address=X.X.X.X
-     */
-    public String status() {
-        return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue;
-    }
-
     public void enableRssiPolling(boolean enabled) {
        sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
     }
@@ -771,7 +759,7 @@
      *
      * @return RSSI value, -1 on failure
      */
-    public int getRssi() {
+    public int syncGetRssi() {
         return sendSyncMessage(CMD_GET_RSSI).intValue;
     }
 
@@ -780,7 +768,7 @@
      *
      * @return RSSI value, -1 on failure
      */
-    public int getRssiApprox() {
+    public int syncGetRssiApprox() {
         return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
     }
 
@@ -789,7 +777,7 @@
      *
      * @return link speed, -1 on failure
      */
-    public int getLinkSpeed() {
+    public int syncGetLinkSpeed() {
         return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
     }
 
@@ -798,7 +786,7 @@
      *
      * @return MAC address, null on failure
      */
-    public String getMacAddress() {
+    public String syncGetMacAddress() {
         return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
     }
 
@@ -895,7 +883,7 @@
      *
      * TODO: deprecate this
      */
-    public boolean saveConfig() {
+    public boolean syncSaveConfig() {
         return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
     }
 
@@ -906,6 +894,37 @@
         sendMessage(CMD_REQUEST_CM_WAKELOCK);
     }
 
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        String LS = System.getProperty("line.separator");
+        sb.append("current HSM state: ").append(getCurrentState().getName()).append(LS);
+        sb.append("mNetworkProperties ").append(mNetworkProperties).append(LS);
+        sb.append("mWifiInfo ").append(mWifiInfo).append(LS);
+        sb.append("mDhcpInfo ").append(mDhcpInfo).append(LS);
+        sb.append("mNetworkInfo ").append(mNetworkInfo).append(LS);
+        sb.append("mNumAllowedChannels ").append(mNumAllowedChannels).append(LS);
+        sb.append("mLastSignalLevel ").append(mLastSignalLevel).append(LS);
+        sb.append("mLastBssid ").append(mLastBssid).append(LS);
+        sb.append("mLastNetworkId ").append(mLastNetworkId).append(LS);
+        sb.append("mLastPriority ").append(mLastPriority).append(LS);
+        sb.append("mEnableAllNetworks ").append(mEnableAllNetworks).append(LS);
+        sb.append("mEnableRssiPolling ").append(mEnableRssiPolling).append(LS);
+        sb.append("mPasswordKeyMayBeIncorrect ").append(mPasswordKeyMayBeIncorrect).append(LS);
+        sb.append("mUseStaticIp ").append(mUseStaticIp).append(LS);
+        sb.append("mReconnectCount ").append(mReconnectCount).append(LS);
+        sb.append("mIsScanMode ").append(mIsScanMode).append(LS);
+        sb.append("mConfigChanged ").append(mConfigChanged).append(LS).append(LS);
+        sb.append("Supplicant status").append(LS)
+                .append(WifiNative.statusCommand()).append(LS).append(LS);
+        sb.append("mConfigChanged ").append(mConfigChanged).append(LS);
+        sb.append("Configured networks ").append(LS);
+        for (WifiConfiguration conf : mConfiguredNetworks) {
+            sb.append(conf).append(LS);
+        }
+        return sb.toString();
+    }
+
     /*********************************************************
      * Internal private functions
      ********************************************************/
@@ -915,7 +934,6 @@
         int intValue;
         String stringValue;
         Object objValue;
-        List<WifiConfiguration> configList;
     }
 
     class SyncParams {
@@ -931,12 +949,10 @@
     }
 
     /**
-     * message.arg2 is reserved to indicate synchronized
      * message.obj is used to store SyncParams
      */
     private SyncReturn syncedSend(Message msg) {
         SyncParams syncParams = (SyncParams) msg.obj;
-        msg.arg2 = SYNCHRONOUS_CALL;
         synchronized(syncParams) {
             if (DBG) Log.d(TAG, "syncedSend " + msg);
             sendMessage(msg);
@@ -995,7 +1011,7 @@
 
         mWifiState.set(wifiState);
 
-        if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName());
+        if (DBG) Log.d(TAG, "setWifiState: " + syncGetWifiStateByName());
 
         final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -1020,7 +1036,7 @@
         // Update state
         mWifiApState.set(wifiApState);
 
-        if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName());
+        if (DBG) Log.d(TAG, "setWifiApState: " + syncGetWifiApStateByName());
 
         final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -1395,6 +1411,7 @@
     }
 
     private void sendConfigChangeBroadcast() {
+        if (!ActivityManagerNative.isSystemReady()) return;
         Intent intent = new Intent(WifiManager.CONFIG_CHANGED_ACTION);
         intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
         mContext.sendBroadcast(intent);
@@ -1413,7 +1430,9 @@
         mContext.sendStickyBroadcast(intent);
     }
 
-    private void sendSupplicantConfigChangedBroadcast() {
+    private void updateConfigAndSendChangeBroadcast() {
+        updateConfiguredNetworks();
+        if (!ActivityManagerNative.isSystemReady()) return;
         Intent intent = new Intent(WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION);
         mContext.sendBroadcast(intent);
     }
@@ -1485,17 +1504,13 @@
     }
 
     private void enableAllNetworks() {
-        if (mEnableAllNetworks) {
-            mEnableAllNetworks = false;
-            List<WifiConfiguration> configList = getConfiguredNetworksNative();
-            for (WifiConfiguration config : configList) {
-                if(config != null && config.status == Status.DISABLED) {
-                    WifiNative.enableNetworkCommand(config.networkId, false);
-                }
+        for (WifiConfiguration config : mConfiguredNetworks) {
+            if(config != null && config.status == Status.DISABLED) {
+                WifiNative.enableNetworkCommand(config.networkId, false);
             }
-            WifiNative.saveConfigCommand();
-            sendSupplicantConfigChangedBroadcast();
         }
+        WifiNative.saveConfigCommand();
+        updateConfigAndSendChangeBroadcast();
     }
 
     private int addOrUpdateNetworkNative(WifiConfiguration config) {
@@ -1689,44 +1704,47 @@
         return -1;
     }
 
-    private List<WifiConfiguration> getConfiguredNetworksNative() {
+    private void updateConfiguredNetworks() {
         String listStr = WifiNative.listNetworksCommand();
         mLastPriority = 0;
-        List<WifiConfiguration> networks =
-            new ArrayList<WifiConfiguration>();
-        if (listStr == null)
-            return networks;
 
-        String[] lines = listStr.split("\n");
-        // Skip the first line, which is a header
-        for (int i = 1; i < lines.length; i++) {
-            String[] result = lines[i].split("\t");
-            // network-id | ssid | bssid | flags
-            WifiConfiguration config = new WifiConfiguration();
-            try {
-                config.networkId = Integer.parseInt(result[0]);
-            } catch(NumberFormatException e) {
-                continue;
-            }
-            if (result.length > 3) {
-                if (result[3].indexOf("[CURRENT]") != -1)
-                    config.status = WifiConfiguration.Status.CURRENT;
-                else if (result[3].indexOf("[DISABLED]") != -1)
-                    config.status = WifiConfiguration.Status.DISABLED;
-                else
+        synchronized (mConfiguredNetworks) {
+            mConfiguredNetworks.clear();
+
+            if (listStr == null)
+                return;
+
+            String[] lines = listStr.split("\n");
+            // Skip the first line, which is a header
+            for (int i = 1; i < lines.length; i++) {
+                String[] result = lines[i].split("\t");
+                // network-id | ssid | bssid | flags
+                WifiConfiguration config = new WifiConfiguration();
+                try {
+                    config.networkId = Integer.parseInt(result[0]);
+                } catch(NumberFormatException e) {
+                    continue;
+                }
+                if (result.length > 3) {
+                    if (result[3].indexOf("[CURRENT]") != -1)
+                        config.status = WifiConfiguration.Status.CURRENT;
+                    else if (result[3].indexOf("[DISABLED]") != -1)
+                        config.status = WifiConfiguration.Status.DISABLED;
+                    else
+                        config.status = WifiConfiguration.Status.ENABLED;
+                } else {
                     config.status = WifiConfiguration.Status.ENABLED;
-            } else {
-                config.status = WifiConfiguration.Status.ENABLED;
+                }
+                readNetworkVariables(config);
+                if (config.priority > mLastPriority) {
+                    mLastPriority = config.priority;
+                }
+                mConfiguredNetworks.add(config);
             }
-            readNetworkVariables(config);
-            if (config.priority > mLastPriority) {
-                mLastPriority = config.priority;
-            }
-            networks.add(config);
         }
-        return networks;
     }
 
+
     /**
      * Read the variables from the supplicant daemon that are needed to
      * fill in the WifiConfiguration object.
@@ -2082,16 +2100,11 @@
                 case CMD_GET_LINK_SPEED:
                 case CMD_GET_MAC_ADDR:
                 case CMD_SAVE_CONFIG:
-                case CMD_CONNECTION_STATUS:
-                case CMD_GET_NETWORK_CONFIG:
-                    if (message.arg2 == SYNCHRONOUS_CALL) {
-                        syncParams = (SyncParams) message.obj;
-                        syncParams.mSyncReturn.boolValue = false;
-                        syncParams.mSyncReturn.intValue = -1;
-                        syncParams.mSyncReturn.stringValue = null;
-                        syncParams.mSyncReturn.configList = null;
-                        notifyOnMsgObject(message);
-                    }
+                    syncParams = (SyncParams) message.obj;
+                    syncParams.mSyncReturn.boolValue = false;
+                    syncParams.mSyncReturn.intValue = -1;
+                    syncParams.mSyncReturn.stringValue = null;
+                    notifyOnMsgObject(message);
                     break;
                 case CMD_ENABLE_RSSI_POLL:
                     mEnableRssiPolling = (message.arg1 == 1);
@@ -2423,6 +2436,9 @@
 
                     mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
 
+                    updateConfiguredNetworks();
+                    enableAllNetworks();
+
                     //TODO: initialize and fix multicast filtering
                     //mWM.initializeMulticastFiltering();
 
@@ -2521,48 +2537,33 @@
                     syncParams = (SyncParams) message.obj;
                     config = (WifiConfiguration) syncParams.mParameter;
                     syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config);
+                    updateConfigAndSendChangeBroadcast();
                     notifyOnMsgObject(message);
-                    sendSupplicantConfigChangedBroadcast();
                     break;
                 case CMD_REMOVE_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-                    if (message.arg2 == SYNCHRONOUS_CALL) {
-                        syncParams = (SyncParams) message.obj;
-                        syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand(
-                                message.arg1);
-                        notifyOnMsgObject(message);
-                    } else {
-                        /* asynchronous handling */
-                        WifiNative.removeNetworkCommand(message.arg1);
-                    }
-                    sendSupplicantConfigChangedBroadcast();
+                    syncParams = (SyncParams) message.obj;
+                    syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand(
+                            message.arg1);
+                    updateConfigAndSendChangeBroadcast();
+                    notifyOnMsgObject(message);
                     break;
                 case CMD_ENABLE_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-                    if (message.arg2 == SYNCHRONOUS_CALL) {
-                        syncParams = (SyncParams) message.obj;
-                        EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
-                        syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand(
-                                enableNetParams.netId, enableNetParams.disableOthers);
-                        notifyOnMsgObject(message);
-                    } else {
-                        /* asynchronous handling */
-                        WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1);
-                    }
-                    sendSupplicantConfigChangedBroadcast();
+                    syncParams = (SyncParams) message.obj;
+                    EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
+                    syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand(
+                            enableNetParams.netId, enableNetParams.disableOthers);
+                    updateConfigAndSendChangeBroadcast();
+                    notifyOnMsgObject(message);
                     break;
                 case CMD_DISABLE_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-                    if (message.arg2 == SYNCHRONOUS_CALL) {
-                        syncParams = (SyncParams) message.obj;
-                        syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand(
-                                message.arg1);
-                        notifyOnMsgObject(message);
-                    } else {
-                        /* asynchronous handling */
-                        WifiNative.disableNetworkCommand(message.arg1);
-                    }
-                    sendSupplicantConfigChangedBroadcast();
+                    syncParams = (SyncParams) message.obj;
+                    syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand(
+                            message.arg1);
+                    updateConfigAndSendChangeBroadcast();
+                    notifyOnMsgObject(message);
                     break;
                 case CMD_BLACKLIST_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
@@ -2571,20 +2572,10 @@
                 case CMD_CLEAR_BLACKLIST:
                     WifiNative.clearBlacklistCommand();
                     break;
-                case CMD_GET_NETWORK_CONFIG:
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.configList = getConfiguredNetworksNative();
-                    notifyOnMsgObject(message);
-                    break;
                 case CMD_SAVE_CONFIG:
-                    if (message.arg2 == SYNCHRONOUS_CALL) {
-                        syncParams = (SyncParams) message.obj;
-                        syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand();
-                        notifyOnMsgObject(message);
-                    } else {
-                        /* asynchronous handling */
-                        WifiNative.saveConfigCommand();
-                    }
+                    syncParams = (SyncParams) message.obj;
+                    syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand();
+                    notifyOnMsgObject(message);
                     // Inform the backup manager about a data change
                     IBackupManager ibm = IBackupManager.Stub.asInterface(
                             ServiceManager.getService(Context.BACKUP_SERVICE));
@@ -2596,11 +2587,6 @@
                         }
                     }
                     break;
-                case CMD_CONNECTION_STATUS:
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.stringValue = WifiNative.statusCommand();
-                    notifyOnMsgObject(message);
-                    break;
                 case CMD_GET_MAC_ADDR:
                     syncParams = (SyncParams) message.obj;
                     syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
@@ -2623,12 +2609,12 @@
                         WifiNative.enableNetworkCommand(netId, false);
                     }
                     WifiNative.saveConfigCommand();
-                    sendSupplicantConfigChangedBroadcast();
+                    updateConfigAndSendChangeBroadcast();
                     break;
                 case CMD_FORGET_NETWORK:
                     WifiNative.removeNetworkCommand(message.arg1);
                     WifiNative.saveConfigCommand();
-                    sendSupplicantConfigChangedBroadcast();
+                    updateConfigAndSendChangeBroadcast();
                     break;
                 default:
                     return NOT_HANDLED;
@@ -2905,8 +2891,7 @@
                     }
                     // Reset the priority of each network at start or if it goes too high.
                     if (mLastPriority == -1 || mLastPriority > 1000000) {
-                        List<WifiConfiguration> configList = getConfiguredNetworksNative();
-                        for (WifiConfiguration conf : configList) {
+                        for (WifiConfiguration conf : mConfiguredNetworks) {
                             if (conf.networkId != -1) {
                                 conf.priority = 0;
                                 addOrUpdateNetworkNative(conf);
@@ -2923,14 +2908,25 @@
                     addOrUpdateNetworkNative(config);
                     WifiNative.saveConfigCommand();
 
-                    // Connect to network by disabling others.
-                    WifiNative.enableNetworkCommand(netId, true);
-                    WifiNative.reconnectCommand();
-                    mEnableAllNetworks = true;
-                    /* Dont send a supplicant config change broadcast here
-                     * as it is better to not expose the temporary disabling
-                     * of all networks
+                    /* We connect to a specific network by first enabling that network
+                     * and disabling all other networks in the supplicant. Disabling a
+                     * connected network will cause a disconnection from the network.
+                     * A reconnectCommand() will then initiate a connection to the enabled
+                     * network.
                      */
+                    WifiNative.enableNetworkCommand(netId, true);
+                    /* Save a flag to indicate that we need to enable all
+                     * networks after supplicant indicates a network
+                     * state change event
+                     */
+                    mEnableAllNetworks = true;
+                    WifiNative.reconnectCommand();
+                    /* update the configured networks but not send a
+                     * broadcast to avoid a fetch from settings
+                     * during this temporary disabling of networks
+                     */
+                    updateConfiguredNetworks();
+                    transitionTo(mDisconnectingState);
                     break;
                 case SCAN_RESULTS_EVENT:
                     /* Set the scan setting back to "connect" mode */
@@ -2946,16 +2942,13 @@
                     mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
                     mWifiInfo.setNetworkId(stateChangeResult.networkId);
                     mLastNetworkId = stateChangeResult.networkId;
-                    enableAllNetworks();
                     /* send event to CM & network change broadcast */
                     setDetailedState(DetailedState.OBTAINING_IPADDR);
                     sendNetworkStateChangeBroadcast(mLastBssid);
-
                     transitionTo(mConnectingState);
                     break;
                 case NETWORK_DISCONNECTION_EVENT:
                     Log.d(TAG,"Network connection lost");
-                    enableAllNetworks();
                     handleNetworkDisconnect();
                     transitionTo(mDisconnectedState);
                     break;
@@ -3090,7 +3083,7 @@
                           Log.e(TAG, "Failed " +
                                   mReconnectCount + " times, Disabling " + mLastNetworkId);
                       WifiNative.disableNetworkCommand(mLastNetworkId);
-                      sendSupplicantConfigChangedBroadcast();
+                      updateConfigAndSendChangeBroadcast();
                   }
 
                   /* DHCP times out after about 30 seconds, we do a
@@ -3104,6 +3097,13 @@
                   WifiNative.disconnectCommand();
                   transitionTo(mDisconnectingState);
                   break;
+                  /* Ignore connection to same network */
+              case CMD_CONNECT_NETWORK:
+                  int netId = message.arg1;
+                  if (mWifiInfo.getNetworkId() == netId) {
+                      break;
+                  }
+                  return NOT_HANDLED;
                   /* Ignore */
               case NETWORK_CONNECTION_EVENT:
                   break;
@@ -3174,6 +3174,13 @@
                         deferMessage(message);
                     }
                     break;
+                    /* Ignore connection to same network */
+                case CMD_CONNECT_NETWORK:
+                    int netId = message.arg1;
+                    if (mWifiInfo.getNetworkId() == netId) {
+                        break;
+                    }
+                    return NOT_HANDLED;
                     /* Ignore */
                 case NETWORK_CONNECTION_EVENT:
                     break;
@@ -3209,6 +3216,13 @@
             EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
+        @Override
+        public void exit() {
+            if (mEnableAllNetworks) {
+                mEnableAllNetworks = false;
+                enableAllNetworks();
+            }
+        }
     }
 
     class DisconnectedState extends HierarchicalState {
@@ -3503,7 +3517,7 @@
                      WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId());
                      mPasswordKeyMayBeIncorrect = false;
                      sendSupplicantStateChangedBroadcast(stateChangeResult, true);
-                     sendSupplicantConfigChangedBroadcast();
+                     updateConfigAndSendChangeBroadcast();
                  }
                  else {
                      sendSupplicantStateChangedBroadcast(stateChangeResult, false);
@@ -3563,7 +3577,7 @@
                  }
                  if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
                      WifiNative.disableNetworkCommand(stateChangeResult.networkId);
-                     sendSupplicantConfigChangedBroadcast();
+                     updateConfigAndSendChangeBroadcast();
                      mLoopDetectCount = 0;
                  }