Merge "Add a Settings table to BrowserContract."
diff --git a/api/current.xml b/api/current.xml
index 2c41aeb..7e7131b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -26685,6 +26685,17 @@
  visibility="public"
 >
 </method>
+<method name="getContext"
+ return="android.content.Context"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="setAdapter"
  return="android.app.AlertDialog.Builder"
  abstract="false"
@@ -28643,6 +28654,623 @@
 >
 </field>
 </class>
+<class name="DownloadManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="enqueue"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="request" type="android.app.DownloadManager.Request">
+</parameter>
+</method>
+<method name="openDownloadedFile"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="long">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+</method>
+<method name="query"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="query" type="android.app.DownloadManager.Query">
+</parameter>
+</method>
+<method name="remove"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="long">
+</parameter>
+</method>
+<field name="ACTION_DOWNLOAD_COMPLETE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.DOWNLOAD_COMPLETE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_NOTIFICATION_CLICKED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_VIEW_DOWNLOADS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.VIEW_DOWNLOADS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_BYTES_DOWNLOADED_SO_FAR"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;bytes_so_far&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_DESCRIPTION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;description&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_ERROR_CODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;error_code&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_LAST_MODIFIED_TIMESTAMP"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;last_modified_timestamp&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_LOCAL_URI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;local_uri&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_MEDIA_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;media_type&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_STATUS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;status&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_TITLE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;title&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_TOTAL_SIZE_BYTES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;total_size&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_URI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;uri&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CANNOT_RESUME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1008"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_DEVICE_NOT_FOUND"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1007"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_FILE_ALREADY_EXISTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1009"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_FILE_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1001"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_HTTP_DATA_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1004"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_INSUFFICIENT_SPACE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1006"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_TOO_MANY_REDIRECTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1005"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_UNHANDLED_HTTP_CODE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1002"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DOWNLOAD_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;extra_download_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_FAILED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_PAUSED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_PENDING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_RUNNING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_SUCCESSFUL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DownloadManager.Query"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DownloadManager.Query"
+ type="android.app.DownloadManager.Query"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="setFilterById"
+ return="android.app.DownloadManager.Query"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="long">
+</parameter>
+</method>
+<method name="setFilterByStatus"
+ return="android.app.DownloadManager.Query"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+</class>
+<class name="DownloadManager.Request"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DownloadManager.Request"
+ type="android.app.DownloadManager.Request"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</constructor>
+<method name="addRequestHeader"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
+<method name="setAllowedNetworkTypes"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="setAllowedOverRoaming"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="allowed" type="boolean">
+</parameter>
+</method>
+<method name="setDescription"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="description" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setDestinationInExternalFilesDir"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="dirType" type="java.lang.String">
+</parameter>
+<parameter name="subPath" type="java.lang.String">
+</parameter>
+</method>
+<method name="setDestinationInExternalPublicDir"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dirType" type="java.lang.String">
+</parameter>
+<parameter name="subPath" type="java.lang.String">
+</parameter>
+</method>
+<method name="setDestinationUri"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="setMimeType"
+ return="android.app.DownloadManager.Request"
+ 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="setShowRunningNotification"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="show" type="boolean">
+</parameter>
+</method>
+<method name="setTitle"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setVisibleInDownloadsUi"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isVisible" type="boolean">
+</parameter>
+</method>
+<field name="NETWORK_MOBILE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NETWORK_WIFI"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="ExpandableListActivity"
  extends="android.app.Activity"
  abstract="false"
@@ -59866,6 +60494,118 @@
 >
 </field>
 </class>
+<class name="ObbInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ 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="parcelableFlags" 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="OBB_OVERLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="flags"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="packageName"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="version"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="ObbScanner"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getObbInfo"
+ return="android.content.res.ObbInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="filePath" type="java.lang.String">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
 <class name="Resources"
  extends="java.lang.Object"
  abstract="false"
@@ -105896,623 +106636,6 @@
 >
 </field>
 </class>
-<class name="DownloadManager"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="enqueue"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="request" type="android.net.DownloadManager.Request">
-</parameter>
-</method>
-<method name="openDownloadedFile"
- return="android.os.ParcelFileDescriptor"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="long">
-</parameter>
-<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
-</exception>
-</method>
-<method name="query"
- return="android.database.Cursor"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="query" type="android.net.DownloadManager.Query">
-</parameter>
-</method>
-<method name="remove"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="long">
-</parameter>
-</method>
-<field name="ACTION_DOWNLOAD_COMPLETE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.DOWNLOAD_COMPLETE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_NOTIFICATION_CLICKED"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_VIEW_DOWNLOADS"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.VIEW_DOWNLOADS&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_BYTES_DOWNLOADED_SO_FAR"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;bytes_so_far&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_DESCRIPTION"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;description&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_ERROR_CODE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;error_code&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_ID"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;_id&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_LAST_MODIFIED_TIMESTAMP"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;last_modified_timestamp&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_LOCAL_URI"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;local_uri&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_MEDIA_TYPE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;media_type&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_STATUS"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;status&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_TITLE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;title&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_TOTAL_SIZE_BYTES"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;total_size&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_URI"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;uri&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_CANNOT_RESUME"
- type="int"
- transient="false"
- volatile="false"
- value="1008"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_DEVICE_NOT_FOUND"
- type="int"
- transient="false"
- volatile="false"
- value="1007"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_FILE_ALREADY_EXISTS"
- type="int"
- transient="false"
- volatile="false"
- value="1009"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_FILE_ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="1001"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_HTTP_DATA_ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="1004"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_INSUFFICIENT_SPACE"
- type="int"
- transient="false"
- volatile="false"
- value="1006"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_TOO_MANY_REDIRECTS"
- type="int"
- transient="false"
- volatile="false"
- value="1005"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_UNHANDLED_HTTP_CODE"
- type="int"
- transient="false"
- volatile="false"
- value="1002"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_UNKNOWN"
- type="int"
- transient="false"
- volatile="false"
- value="1000"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_DOWNLOAD_ID"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;extra_download_id&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_FAILED"
- type="int"
- transient="false"
- volatile="false"
- value="16"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_PAUSED"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_PENDING"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_RUNNING"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_SUCCESSFUL"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="DownloadManager.Query"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="DownloadManager.Query"
- type="android.net.DownloadManager.Query"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="setFilterById"
- return="android.net.DownloadManager.Query"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="long">
-</parameter>
-</method>
-<method name="setFilterByStatus"
- return="android.net.DownloadManager.Query"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flags" type="int">
-</parameter>
-</method>
-</class>
-<class name="DownloadManager.Request"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="DownloadManager.Request"
- type="android.net.DownloadManager.Request"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-</constructor>
-<method name="addRequestHeader"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="header" type="java.lang.String">
-</parameter>
-<parameter name="value" type="java.lang.String">
-</parameter>
-</method>
-<method name="setAllowedNetworkTypes"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<method name="setAllowedOverRoaming"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="allowed" type="boolean">
-</parameter>
-</method>
-<method name="setDescription"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="description" type="java.lang.CharSequence">
-</parameter>
-</method>
-<method name="setDestinationInExternalFilesDir"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="dirType" type="java.lang.String">
-</parameter>
-<parameter name="subPath" type="java.lang.String">
-</parameter>
-</method>
-<method name="setDestinationInExternalPublicDir"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dirType" type="java.lang.String">
-</parameter>
-<parameter name="subPath" type="java.lang.String">
-</parameter>
-</method>
-<method name="setDestinationUri"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-</method>
-<method name="setMimeType"
- return="android.net.DownloadManager.Request"
- 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="setShowRunningNotification"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="show" type="boolean">
-</parameter>
-</method>
-<method name="setTitle"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="title" type="java.lang.CharSequence">
-</parameter>
-</method>
-<method name="setVisibleInDownloadsUi"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="isVisible" type="boolean">
-</parameter>
-</method>
-<field name="NETWORK_MOBILE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NETWORK_WIFI"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="LocalServerSocket"
  extends="java.lang.Object"
  abstract="false"
@@ -139659,6 +139782,38 @@
 </package>
 <package name="android.os.storage"
 >
+<class name="OnObbStateChangeListener"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="OnObbStateChangeListener"
+ type="android.os.storage.OnObbStateChangeListener"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onObbStateChange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="state" type="java.lang.String">
+</parameter>
+</method>
+</class>
 <class name="StorageEventListener"
  extends="java.lang.Object"
  abstract="true"
@@ -139800,6 +139955,8 @@
 </parameter>
 <parameter name="key" type="java.lang.String">
 </parameter>
+<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
+</parameter>
 </method>
 <method name="registerListener"
  return="void"
@@ -139828,6 +139985,8 @@
 </parameter>
 <parameter name="force" type="boolean">
 </parameter>
+<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
+</parameter>
 <exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
 </exception>
 </method>
@@ -240986,7 +241145,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
@@ -305890,7 +306049,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="x" type="java.sql.Blob">
+<parameter name="blob" type="java.sql.Blob">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -306049,7 +306208,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="x" type="java.sql.Clob">
+<parameter name="clob" type="java.sql.Clob">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -306187,7 +306346,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="value" type="java.io.Reader">
+<parameter name="reader" type="java.io.Reader">
 </parameter>
 <parameter name="length" type="long">
 </parameter>
@@ -306223,7 +306382,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="value" type="java.sql.NClob">
+<parameter name="nclob" type="java.sql.NClob">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -306276,7 +306435,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="value" type="java.lang.String">
+<parameter name="string" type="java.lang.String">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -306386,7 +306545,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="x" type="java.sql.RowId">
+<parameter name="rowId" type="java.sql.RowId">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -306403,7 +306562,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="xmlObject" type="java.sql.SQLXML">
+<parameter name="sqlXml" type="java.sql.SQLXML">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -311539,7 +311698,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="x" type="java.io.InputStream">
+<parameter name="inputStream" type="java.io.InputStream">
 </parameter>
 <parameter name="length" type="long">
 </parameter>
@@ -311558,7 +311717,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="x" type="java.io.InputStream">
+<parameter name="inputStream" type="java.io.InputStream">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -311611,7 +311770,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="x" type="java.io.InputStream">
+<parameter name="inputStream" type="java.io.InputStream">
 </parameter>
 <parameter name="length" type="long">
 </parameter>
@@ -311630,7 +311789,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="x" type="java.io.InputStream">
+<parameter name="inputStream" type="java.io.InputStream">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -311963,7 +312122,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="value" type="java.io.Reader">
+<parameter name="reader" type="java.io.Reader">
 </parameter>
 <parameter name="length" type="long">
 </parameter>
@@ -311982,7 +312141,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="value" type="java.io.Reader">
+<parameter name="reader" type="java.io.Reader">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -312052,7 +312211,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="value" type="java.lang.String">
+<parameter name="theString" type="java.lang.String">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -312179,7 +312338,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="x" type="java.sql.RowId">
+<parameter name="theRowId" type="java.sql.RowId">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -315173,7 +315332,7 @@
 >
 <parameter name="columnIndex" type="int">
 </parameter>
-<parameter name="x" type="java.sql.RowId">
+<parameter name="value" type="java.sql.RowId">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -315190,7 +315349,7 @@
 >
 <parameter name="columnLabel" type="java.lang.String">
 </parameter>
-<parameter name="x" type="java.sql.RowId">
+<parameter name="value" type="java.sql.RowId">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -317522,7 +317681,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="x" type="java.sql.NClob">
+<parameter name="theNClob" type="java.sql.NClob">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -317537,7 +317696,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="x" type="java.lang.String">
+<parameter name="theString" type="java.lang.String">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -317582,7 +317741,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="x" type="java.sql.RowId">
+<parameter name="theRowId" type="java.sql.RowId">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -317597,7 +317756,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="x" type="java.sql.SQLXML">
+<parameter name="theXml" type="java.sql.SQLXML">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 8b54871..f55b746 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -46,6 +46,7 @@
 #include <media/mediametadataretriever.h>
 
 #include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MPEG2TSWriter.h>
 #include <media/stagefright/MPEG4Writer.h>
 
 #include <fcntl.h>
@@ -366,8 +367,13 @@
 
 static void writeSourcesToMP4(
         Vector<sp<MediaSource> > &sources, bool syncInfoPresent) {
+#if 0
     sp<MPEG4Writer> writer =
         new MPEG4Writer(gWriteMP4Filename.string());
+#else
+    sp<MPEG2TSWriter> writer =
+        new MPEG2TSWriter(gWriteMP4Filename.string());
+#endif
 
     // at most one minute.
     writer->setMaxFileDuration(60000000ll);
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 92cedc4..f0477e5 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import com.android.internal.app.AlertController;
+
 import android.content.Context;
 import android.content.DialogInterface;
 import android.database.Cursor;
@@ -23,6 +25,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Message;
+import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.WindowManager;
@@ -31,8 +34,6 @@
 import android.widget.ListAdapter;
 import android.widget.ListView;
 
-import com.android.internal.app.AlertController;
-
 /**
  * A subclass of Dialog that can display one, two or three buttons. If you only want to
  * display a String in this dialog box, use the setMessage() method.  If you
@@ -273,6 +274,7 @@
     public static class Builder {
         private final AlertController.AlertParams P;
         private int mTheme;
+        private Context mWrappedContext;
         
         /**
          * Constructor using a context for this builder and the {@link AlertDialog} it creates.
@@ -294,6 +296,21 @@
         }
         
         /**
+         * Returns a {@link Context} with the appropriate theme for dialogs created by this Builder.
+         * Applications should use this Context for obtaining LayoutInflaters for inflating views
+         * that will be used in the resulting dialogs, as it will cause views to be inflated with
+         * the correct theme.
+         *
+         * @return A Context for built Dialogs.
+         */
+        public Context getContext() {
+            if (mWrappedContext == null) {
+                mWrappedContext = new ContextThemeWrapper(P.mContext, mTheme);
+            }
+            return mWrappedContext;
+        }
+
+        /**
          * Set the title using the given resource id.
          *
          * @return This Builder object to allow for chaining of calls to set methods
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5afea13..3174f55 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -69,7 +69,6 @@
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
-import android.net.DownloadManager;
 import android.net.ThrottleManager;
 import android.net.IThrottleManager;
 import android.net.Uri;
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 04735bf..a178c04 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -177,7 +177,7 @@
     /**
      * Retrieve the Context this Dialog is running in.
      * 
-     * @return Context The Context that was supplied to the constructor.
+     * @return Context The Context used by the Dialog.
      */
     public final Context getContext() {
         return mContext;
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/app/DownloadManager.java
similarity index 99%
rename from core/java/android/net/DownloadManager.java
rename to core/java/android/app/DownloadManager.java
index 3279e8f..69c99cc 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.app;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -22,6 +22,8 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.database.CursorWrapper;
+import android.net.ConnectivityManager;
+import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.provider.BaseColumns;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3d29379..3a56afb 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1235,7 +1235,7 @@
      * <dt> {@link #UI_MODE_SERVICE} ("uimode")
      * <dd> An {@link android.app.UiModeManager} for controlling UI modes.
      * <dt> {@link #DOWNLOAD_SERVICE} ("download")
-     * <dd> A {@link android.net.DownloadManager} for requesting HTTP downloads
+     * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads
      * </dl>
      * 
      * <p>Note:  System services obtained via this API may be closely associated with
@@ -1284,7 +1284,7 @@
      * @see #UI_MODE_SERVICE
      * @see android.app.UiModeManager
      * @see #DOWNLOAD_SERVICE
-     * @see android.net.DownloadManager
+     * @see android.app.DownloadManager
      */
     public abstract Object getSystemService(String name);
 
@@ -1576,7 +1576,7 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
-     * {@link android.net.DownloadManager} for requesting HTTP downloads.
+     * {@link android.app.DownloadManager} for requesting HTTP downloads.
      *
      * @see #getSystemService
      */
diff --git a/core/java/android/content/res/ObbInfo.java b/core/java/android/content/res/ObbInfo.java
index 838c5ff..7b962e5 100644
--- a/core/java/android/content/res/ObbInfo.java
+++ b/core/java/android/content/res/ObbInfo.java
@@ -20,9 +20,9 @@
 import android.os.Parcelable;
 
 /**
- * Basic information about a Opaque Binary Blob (OBB) that reflects
- * the info from the footer on the OBB file.
- * @hide
+ * Basic information about a Opaque Binary Blob (OBB) that reflects the info
+ * from the footer on the OBB file. This information may be manipulated by a
+ * developer with the <code>obbtool</code> program in the Android SDK.
  */
 public class ObbInfo implements Parcelable {
     /** Flag noting that this OBB is an overlay patch for a base OBB. */
@@ -43,7 +43,8 @@
      */
     public int flags;
 
-    public ObbInfo() {
+    // Only allow things in this package to instantiate.
+    /* package */ ObbInfo() {
     }
 
     public String toString() {
diff --git a/core/java/android/content/res/ObbScanner.java b/core/java/android/content/res/ObbScanner.java
index eb383c3..a3f141e 100644
--- a/core/java/android/content/res/ObbScanner.java
+++ b/core/java/android/content/res/ObbScanner.java
@@ -16,25 +16,43 @@
 
 package android.content.res;
 
+import java.io.File;
+import java.io.IOException;
+
 /**
- * Class to scan Opaque Binary Blob (OBB) files.
- * @hide
+ * Class to scan Opaque Binary Blob (OBB) files. Use this to get information
+ * about an OBB file for use in a program via {@link ObbInfo}.
  */
 public class ObbScanner {
     // Don't allow others to instantiate this class
     private ObbScanner() {}
 
-    public static ObbInfo getObbInfo(String filePath) {
+    /**
+     * Scan a file for OBB information.
+     * 
+     * @param filePath path to the OBB file to be scanned.
+     * @return ObbInfo object information corresponding to the file path
+     * @throws IllegalArgumentException if the OBB file couldn't be found
+     * @throws IOException if the OBB file couldn't be read
+     */
+    public static ObbInfo getObbInfo(String filePath) throws IOException {
         if (filePath == null) {
-            return null;
+            throw new IllegalArgumentException("file path cannot be null");
         }
 
-        ObbInfo obbInfo = new ObbInfo();
-        if (!getObbInfo_native(filePath, obbInfo)) {
-            throw new IllegalArgumentException("Could not read OBB file: " + filePath);
+        final File obbFile = new File(filePath);
+        if (!obbFile.exists()) {
+            throw new IllegalArgumentException("OBB file does nto exist: " + filePath);
         }
+
+        final String canonicalFilePath = obbFile.getCanonicalPath();
+
+        ObbInfo obbInfo = new ObbInfo();
+        getObbInfo_native(canonicalFilePath, obbInfo);
+
         return obbInfo;
     }
 
-    private native static boolean getObbInfo_native(String filePath, ObbInfo obbInfo);
+    private native static void getObbInfo_native(String filePath, ObbInfo obbInfo)
+            throws IOException;
 }
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 588384b..6ed1a90 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -88,13 +88,9 @@
         mInUse = false;
     }
 
-    /* package */ synchronized boolean isInUse() {
-        return mInUse;
-    }
-
     /* package */ synchronized void releaseIfNotInUse() {
         // if it is not in use, release its memory from the database
-        if (!isInUse()) {
+        if (!mInUse) {
             releaseSqlStatement();
         }
     }
@@ -110,7 +106,7 @@
             // but if the database itself is not closed and is GC'ed, then
             // all sub-objects attached to the database could end up getting GC'ed too.
             // in that case, don't print any warning.
-            if (!mInUse) {
+            if (mInUse) {
                 int len = mSqlStmt.length();
                 Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
                         "that you explicitly call close() on your cursor: " +
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 7ab5d82..f0d0fb4 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -276,6 +276,7 @@
      */
     // default statement-cache size per database connection ( = instance of this class)
     private int mMaxSqlCacheSize = 25;
+    // guarded by itself
     /* package */ final Map<String, SQLiteCompiledSql> mCompiledQueries =
         new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
             @Override
@@ -306,8 +307,9 @@
     public static final int MAX_SQL_CACHE_SIZE = 100;
     private boolean mCacheFullWarning;
 
-    /** maintain stats about number of cache hits and misses */
+    /** Number of cache hits on this database connection. guarded by {@link #mCompiledQueries}. */
     private int mNumCacheHits;
+    /** Number of cache misses on this database connection. guarded by {@link #mCompiledQueries}. */
     private int mNumCacheMisses;
 
     /** Used to find out where this object was created in case it never got closed. */
@@ -344,6 +346,9 @@
 
     private static final String MEMORY_DB_PATH = ":memory:";
 
+    /** set to true if the database has attached databases */
+    private volatile boolean mHasAttachedDbs = false;
+
     /** stores reference to all databases opened in the current process. */
     private static ArrayList<WeakReference<SQLiteDatabase>> mActiveDatabases =
             new ArrayList<WeakReference<SQLiteDatabase>>();
@@ -1836,6 +1841,9 @@
         logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
         executeSql(sql, null);
 
+        if (stmtType == DatabaseUtils.STATEMENT_ATTACH) {
+            mHasAttachedDbs = true;
+        }
         // Log commit statements along with the most recently executed
         // SQL statement for disambiguation.
         if (stmtType == DatabaseUtils.STATEMENT_COMMIT) {
@@ -2183,22 +2191,34 @@
         }
     }
 
-    /* package */ boolean isInStatementCache(SQLiteCompiledSql sqliteCompiledSql) {
+    /* package */ void releaseCompiledSqlObj(SQLiteCompiledSql compiledSql) {
         synchronized (mCompiledQueries) {
-            return mCompiledQueries.containsValue(sqliteCompiledSql);
+            if (mCompiledQueries.containsValue(compiledSql)) {
+                // it is in cache - reset its inUse flag
+                compiledSql.release();
+            } else {
+                // it is NOT in cache. finalize it.
+                compiledSql.releaseSqlStatement();
+            }
         }
     }
 
-    private synchronized int getCacheHitNum() {
-        return mNumCacheHits;
+    private int getCacheHitNum() {
+        synchronized(mCompiledQueries) {
+            return mNumCacheHits;
+        }
     }
 
-    private synchronized int getCacheMissNum() {
-        return mNumCacheMisses;
+    private int getCacheMissNum() {
+        synchronized(mCompiledQueries) {
+            return mNumCacheMisses;
+        }
     }
 
-    private synchronized int getCachesize() {
-        return mCompiledQueries.size();
+    private int getCachesize() {
+        synchronized(mCompiledQueries) {
+            return mCompiledQueries.size();
+        }
     }
 
     /* package */ void finalizeStatementLater(int id) {
@@ -2285,7 +2305,13 @@
      * @return true if write-ahead-logging is set. false otherwise
      */
     public boolean enableWriteAheadLogging() {
-        synchronized(this) {
+        // acquire lock - no that no other thread is enabling WAL at the same time
+        lock();
+        try {
+            if (mConnectionPool != null) {
+                // already enabled
+                return true;
+            }
             if (mPath.equalsIgnoreCase(MEMORY_DB_PATH)) {
                 Log.i(TAG, "can't enable WAL for memory databases.");
                 return false;
@@ -2293,18 +2319,18 @@
 
             // make sure this database has NO attached databases because sqlite's write-ahead-logging
             // doesn't work for databases with attached databases
-            if (getAttachedDbs().size() > 1) {
+            if (mHasAttachedDbs) {
                 if (Log.isLoggable(TAG, Log.DEBUG)) {
                     Log.d(TAG,
                             "this database: " + mPath + " has attached databases. can't  enable WAL.");
                 }
                 return false;
             }
-            if (mConnectionPool == null) {
-                mConnectionPool = new DatabaseConnectionPool(this);
-                setJournalMode(mPath, "WAL");
-            }
+            mConnectionPool = new DatabaseConnectionPool(this);
+            setJournalMode(mPath, "WAL");
             return true;
+        } finally {
+            unlock();
         }
     }
 
@@ -2313,13 +2339,18 @@
      * @hide
      */
     public void disableWriteAheadLogging() {
-        synchronized (this) {
+        // grab database lock so that writeAheadLogging is not disabled from 2 different threads
+        // at the same time
+        lock();
+        try {
             if (mConnectionPool == null) {
-                return;
+                return; // already disabled
             }
             mConnectionPool.close();
-            mConnectionPool = null;
             setJournalMode(mPath, "TRUNCATE");
+            mConnectionPool = null;
+        } finally {
+            unlock();
         }
     }
 
@@ -2409,73 +2440,74 @@
      */
     /* package */ static ArrayList<DbStats> getDbStats() {
         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;
-//        synchronized(mActiveDatabases) {
-//            tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone();
-//        }
-//        for (WeakReference<SQLiteDatabase> w : tempList) {
-//            SQLiteDatabase db = w.get();
-//            if (db == null || !db.isOpen()) {
-//                continue;
-//            }
-//
-//            synchronized (db) {
-//                try {
-//                    // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
-//                    int lookasideUsed = db.native_getDbLookaside();
-//
-//                    // get the lastnode of the dbname
-//                    String path = db.getPath();
-//                    int indx = path.lastIndexOf("/");
-//                    String lastnode = path.substring((indx != -1) ? ++indx : 0);
-//
-//                    // get list of attached dbs and for each db, get its size and pagesize
-//                    ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs();
-//                    if (attachedDbs == null) {
-//                        continue;
-//                    }
-//                    for (int i = 0; i < attachedDbs.size(); i++) {
-//                        Pair<String, String> p = attachedDbs.get(i);
-//                        long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first
-//                                + ".page_count;", null);
-//
-//                        // first entry in the attached db list is always the main database
-//                        // don't worry about prefixing the dbname with "main"
-//                        String dbName;
-//                        if (i == 0) {
-//                            dbName = lastnode;
-//                        } else {
-//                            // lookaside is only relevant for the main db
-//                            lookasideUsed = 0;
-//                            dbName = "  (attached) " + p.first;
-//                            // if the attached db has a path, attach the lastnode from the path to above
-//                            if (p.second.trim().length() > 0) {
-//                                int idx = p.second.lastIndexOf("/");
-//                                dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
-//                            }
-//                        }
-//                        if (pageCount > 0) {
-//                            dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
-//                                    lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
-//                                    db.getCachesize()));
-//                        }
-//                    }
-//                    // if there are pooled connections, return the cache stats for them also.
-//                    if (db.mConnectionPool != null) {
-//                        for (SQLiteDatabase pDb : db.mConnectionPool.getConnectionList()) {
-//                            dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
-//                                    + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
-//                                    pDb.getCacheMissNum(), pDb.getCachesize()));
-//                        }
-//                    }
-//                } catch (SQLiteException e) {
-//                    // ignore. we don't care about exceptions when we are taking adb
-//                    // bugreport!
-//                }
-//            }
-//        }
+        // make a local copy of mActiveDatabases - so that this method is not competing
+        // for synchronization lock on mActiveDatabases
+        ArrayList<WeakReference<SQLiteDatabase>> tempList;
+        synchronized(mActiveDatabases) {
+            tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone();
+        }
+        for (WeakReference<SQLiteDatabase> w : tempList) {
+            SQLiteDatabase db = w.get();
+            if (db == null || !db.isOpen()) {
+                continue;
+            }
+
+            try {
+                // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
+                int lookasideUsed = db.native_getDbLookaside();
+
+                // get the lastnode of the dbname
+                String path = db.getPath();
+                int indx = path.lastIndexOf("/");
+                String lastnode = path.substring((indx != -1) ? ++indx : 0);
+
+                // get list of attached dbs and for each db, get its size and pagesize
+                ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs();
+                if (attachedDbs == null) {
+                    continue;
+                }
+                for (int i = 0; i < attachedDbs.size(); i++) {
+                    Pair<String, String> p = attachedDbs.get(i);
+                    long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first
+                            + ".page_count;", null);
+
+                    // first entry in the attached db list is always the main database
+                    // don't worry about prefixing the dbname with "main"
+                    String dbName;
+                    if (i == 0) {
+                        dbName = lastnode;
+                    } else {
+                        // lookaside is only relevant for the main db
+                        lookasideUsed = 0;
+                        dbName = "  (attached) " + p.first;
+                        // if the attached db has a path, attach the lastnode from the path to above
+                        if (p.second.trim().length() > 0) {
+                            int idx = p.second.lastIndexOf("/");
+                            dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
+                        }
+                    }
+                    if (pageCount > 0) {
+                        dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
+                                lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
+                                db.getCachesize()));
+                    }
+                }
+                // if there are pooled connections, return the cache stats for them also.
+                // while we are trying to query the pooled connections for stats, some other thread
+                // could be disabling conneciton pool. so, grab a reference to the connection pool.
+                DatabaseConnectionPool connPool = db.mConnectionPool;
+                if (connPool != null) {
+                    for (SQLiteDatabase pDb : connPool.getConnectionList()) {
+                        dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
+                                + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
+                                pDb.getCacheMissNum(), pDb.getCachesize()));
+                    }
+                }
+            } catch (SQLiteException e) {
+                // ignore. we don't care about exceptions when we are taking adb
+                // bugreport!
+            }
+        }
         return dbStatsList;
     }
 
@@ -2491,6 +2523,20 @@
             return null;
         }
         ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
+        if (!mHasAttachedDbs) {
+            // No attached databases.
+            // There is a small window where attached databases exist but this flag is not set yet.
+            // This can occur when this thread is in a race condition with another thread
+            // that is executing the SQL statement: "attach database <blah> as <foo>"
+            // If this thread is NOT ok with such a race condition (and thus possibly not receive
+            // the entire list of attached databases), then the caller should ensure that no thread
+            // is executing any SQL statements while a thread is calling this method.
+            // Typically, this method is called when 'adb bugreport' is done or the caller wants to
+            // collect stats on the database and all its attached databases.
+            attachedDbs.add(new Pair<String, String>("main", mPath));
+            return attachedDbs;
+        }
+        // has attached databases. query sqlite to get the list of attached databases.
         Cursor c = null;
         try {
             c = rawQuery("pragma database_list;", null);
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index e78e35a..83621f2 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -25,7 +25,7 @@
 /**
  * A base class for compiled SQLite programs.
  *<p>
- * SQLiteProgram is not internally synchronized so code using a SQLiteProgram from multiple
+ * SQLiteProgram is NOT internally synchronized so code using a SQLiteProgram from multiple
  * threads should perform its own synchronization when using the SQLiteProgram.
  */
 public abstract class SQLiteProgram extends SQLiteClosable {
@@ -180,23 +180,11 @@
         mDatabase.releaseReference();
     }
 
-    /* package */ synchronized void release() {
+    /* package */ void release() {
         if (mCompiledSql == null) {
             return;
         }
-        if ((mStatementType & STATEMENT_CACHEABLE) == 0) {
-            // this SQL statement was never in cache
-            mCompiledSql.releaseSqlStatement();
-        } else {
-            if (!mDatabase.isInStatementCache(mCompiledSql)) {
-                // it is NOT in compiled-sql cache. i.e., responsibility of
-                // releasing this statement is on me.
-                mCompiledSql.releaseSqlStatement();
-            } else {
-                // it is in compiled-sql cache. reset its CompiledSql#mInUse flag
-                mCompiledSql.release();
-            }
-        }
+        mDatabase.releaseCompiledSqlObj(mCompiledSql);
         mCompiledSql = null;
         nStatement = 0;
     }
@@ -239,34 +227,32 @@
     }
 
     private void bind(int type, int index, Object value) {
-        synchronized (this) {
-            mDatabase.verifyDbIsOpen();
-            addToBindArgs(index, (type == Cursor.FIELD_TYPE_NULL) ? null : value);
-            if (nStatement > 0) {
-                // bind only if the SQL statement is compiled
-                acquireReference();
-                try {
-                    switch (type) {
-                        case Cursor.FIELD_TYPE_NULL:
-                            native_bind_null(index);
-                            break;
-                        case Cursor.FIELD_TYPE_BLOB:
-                            native_bind_blob(index, (byte[]) value);
-                            break;
-                        case Cursor.FIELD_TYPE_FLOAT:
-                            native_bind_double(index, (Double) value);
-                            break;
-                        case Cursor.FIELD_TYPE_INTEGER:
-                            native_bind_long(index, (Long) value);
-                            break;
-                        case Cursor.FIELD_TYPE_STRING:
-                        default:
-                            native_bind_string(index, (String) value);
-                            break;
-                    }
-                } finally {
-                    releaseReference();
+        mDatabase.verifyDbIsOpen();
+        addToBindArgs(index, (type == Cursor.FIELD_TYPE_NULL) ? null : value);
+        if (nStatement > 0) {
+            // bind only if the SQL statement is compiled
+            acquireReference();
+            try {
+                switch (type) {
+                    case Cursor.FIELD_TYPE_NULL:
+                        native_bind_null(index);
+                        break;
+                    case Cursor.FIELD_TYPE_BLOB:
+                        native_bind_blob(index, (byte[]) value);
+                        break;
+                    case Cursor.FIELD_TYPE_FLOAT:
+                        native_bind_double(index, (Double) value);
+                        break;
+                    case Cursor.FIELD_TYPE_INTEGER:
+                        native_bind_long(index, (Long) value);
+                        break;
+                    case Cursor.FIELD_TYPE_STRING:
+                    default:
+                        native_bind_string(index, (String) value);
+                        break;
                 }
+            } finally {
+                releaseReference();
             }
         }
     }
@@ -335,18 +321,16 @@
      * Clears all existing bindings. Unset bindings are treated as NULL.
      */
     public void clearBindings() {
-        synchronized (this) {
-            mBindArgs = null;
-            if (this.nStatement == 0) {
-                return;
-            }
-            mDatabase.verifyDbIsOpen();
-            acquireReference();
-            try {
-                native_clear_bindings();
-            } finally {
-                releaseReference();
-            }
+        mBindArgs = null;
+        if (this.nStatement == 0) {
+            return;
+        }
+        mDatabase.verifyDbIsOpen();
+        acquireReference();
+        try {
+            native_clear_bindings();
+        } finally {
+            releaseReference();
         }
     }
 
@@ -354,23 +338,21 @@
      * Release this program's resources, making it invalid.
      */
     public void close() {
-        synchronized (this) {
-            mBindArgs = null;
-            if (nHandle == 0 || !mDatabase.isOpen()) {
-                return;
-            }
-            releaseReference();
+        mBindArgs = null;
+        if (nHandle == 0 || !mDatabase.isOpen()) {
+            return;
         }
+        releaseReference();
     }
 
-    private synchronized void addToBindArgs(int index, Object value) {
+    private void addToBindArgs(int index, Object value) {
         if (mBindArgs == null) {
             mBindArgs = new HashMap<Integer, Object>();
         }
         mBindArgs.put(index, value);
     }
 
-    /* package */ synchronized void compileAndbindAllArgs() {
+    /* package */ void compileAndbindAllArgs() {
         if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
             // no need to prepare this SQL statement
             if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
@@ -422,10 +404,8 @@
             return;
         }
         int size = bindArgs.length;
-        synchronized(this) {
-            for (int i = 0; i < size; i++) {
-                bindString(i + 1, bindArgs[i]);
-            }
+        for (int i = 0; i < size; i++) {
+            bindString(i + 1, bindArgs[i]);
         }
     }
 
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index bd05e24..5e96928 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -31,7 +31,7 @@
  * Don't use SQLiteStatement constructor directly, please use
  * {@link SQLiteDatabase#compileStatement(String)}
  *<p>
- * SQLiteStatement is not internally synchronized so code using a SQLiteStatement from multiple
+ * SQLiteStatement is NOT internally synchronized so code using a SQLiteStatement from multiple
  * threads should perform its own synchronization when using the SQLiteStatement.
  */
 @SuppressWarnings("deprecation")
@@ -79,23 +79,21 @@
      *         some reason
      */
     public int executeUpdateDelete() {
-        synchronized(this) {
-            try {
-                long timeStart = acquireAndLock(WRITE);
-                int numChanges = 0;
-                if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
-                    // since the statement doesn't have to be prepared,
-                    // call the following native method which will not prepare
-                    // the query plan
-                    native_executeSql(mSql);
-                } else {
-                    numChanges = native_execute();
-                }
-                mDatabase.logTimeStat(mSql, timeStart);
-                return numChanges;
-            } finally {
-                releaseAndUnlock();
+        try {
+            long timeStart = acquireAndLock(WRITE);
+            int numChanges = 0;
+            if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
+                // since the statement doesn't have to be prepared,
+                // call the following native method which will not prepare
+                // the query plan
+                native_executeSql(mSql);
+            } else {
+                numChanges = native_execute();
             }
+            mDatabase.logTimeStat(mSql, timeStart);
+            return numChanges;
+        } finally {
+            releaseAndUnlock();
         }
     }
 
@@ -109,15 +107,13 @@
      *         some reason
      */
     public long executeInsert() {
-        synchronized(this) {
-            try {
-                long timeStart = acquireAndLock(WRITE);
-                long lastInsertedRowId = native_executeInsert();
-                mDatabase.logTimeStat(mSql, timeStart);
-                return lastInsertedRowId;
-            } finally {
-                releaseAndUnlock();
-            }
+        try {
+            long timeStart = acquireAndLock(WRITE);
+            long lastInsertedRowId = native_executeInsert();
+            mDatabase.logTimeStat(mSql, timeStart);
+            return lastInsertedRowId;
+        } finally {
+            releaseAndUnlock();
         }
     }
 
@@ -130,15 +126,13 @@
      * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
      */
     public long simpleQueryForLong() {
-        synchronized(this) {
-            try {
-                long timeStart = acquireAndLock(READ);
-                long retValue = native_1x1_long();
-                mDatabase.logTimeStat(mSql, timeStart);
-                return retValue;
-            } finally {
-                releaseAndUnlock();
-            }
+        try {
+            long timeStart = acquireAndLock(READ);
+            long retValue = native_1x1_long();
+            mDatabase.logTimeStat(mSql, timeStart);
+            return retValue;
+        } finally {
+            releaseAndUnlock();
         }
     }
 
@@ -151,15 +145,13 @@
      * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
      */
     public String simpleQueryForString() {
-        synchronized(this) {
-            try {
-                long timeStart = acquireAndLock(READ);
-                String retValue = native_1x1_string();
-                mDatabase.logTimeStat(mSql, timeStart);
-                return retValue;
-            } finally {
-                releaseAndUnlock();
-            }
+        try {
+            long timeStart = acquireAndLock(READ);
+            String retValue = native_1x1_string();
+            mDatabase.logTimeStat(mSql, timeStart);
+            return retValue;
+        } finally {
+            releaseAndUnlock();
         }
     }
 
@@ -172,18 +164,16 @@
      * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
      */
     public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() {
-        synchronized(this) {
-            try {
-                long timeStart = acquireAndLock(READ);
-                ParcelFileDescriptor retValue = native_1x1_blob_ashmem();
-                mDatabase.logTimeStat(mSql, timeStart);
-                return retValue;
-            } catch (IOException ex) {
-                Log.e(TAG, "simpleQueryForBlobFileDescriptor() failed", ex);
-                return null;
-            } finally {
-                releaseAndUnlock();
-            }
+        try {
+            long timeStart = acquireAndLock(READ);
+            ParcelFileDescriptor retValue = native_1x1_blob_ashmem();
+            mDatabase.logTimeStat(mSql, timeStart);
+            return retValue;
+        } catch (IOException ex) {
+            Log.e(TAG, "simpleQueryForBlobFileDescriptor() failed", ex);
+            return null;
+        } finally {
+            releaseAndUnlock();
         }
     }
 
diff --git a/core/java/android/os/storage/OnObbStateChangeListener.java b/core/java/android/os/storage/OnObbStateChangeListener.java
new file mode 100644
index 0000000..a2d0a56
--- /dev/null
+++ b/core/java/android/os/storage/OnObbStateChangeListener.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+/**
+ * Used for receiving notifications from {@link StorageManager}.
+ */
+public abstract class OnObbStateChangeListener {
+    /**
+     * Called when an OBB has changed states.
+     * 
+     * @param path path to the OBB file the state change has happened on
+     * @param state the current state of the OBB
+     */
+    public void onObbStateChange(String path, String state) {
+    }
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4a0296b..4268618 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -23,14 +23,28 @@
 import android.os.ServiceManager;
 import android.util.Log;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 
 /**
- * StorageManager is the interface to the systems storage service.
+ * StorageManager is the interface to the systems storage service. The storage
+ * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
+ * <p>
+ * OBBs contain a filesystem that maybe be encrypted on disk and mounted
+ * on-demand from an application. OBBs are a good way of providing large amounts
+ * of binary assets without packaging them into APKs as they may be multiple
+ * gigabytes in size. However, due to their size, they're most likely stored in
+ * a shared storage pool accessible from all programs. The system does not
+ * guarantee the security of the OBB file itself: if any program modifies the
+ * OBB, there is no guarantee that a read from that OBB will produce the
+ * expected output.
+ * <p>
  * Get an instance of this class by calling
- * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
- * of {@link android.content.Context#STORAGE_SERVICE}.
- *
+ * {@link android.content.Context#getSystemService(java.lang.String)} with an
+ * argument of {@link android.content.Context#STORAGE_SERVICE}.
  */
 
 public class StorageManager
@@ -76,11 +90,113 @@
     /**
      * Binder listener for OBB action results.
      */
-    private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener();
-    private class ObbActionBinderListener extends IObbActionListener.Stub {
+    private final ObbActionListener mObbActionListener = new ObbActionListener();
+
+    private class ObbActionListener extends IObbActionListener.Stub {
+        private List<WeakReference<ObbListenerDelegate>> mListeners = new LinkedList<WeakReference<ObbListenerDelegate>>();
+
         @Override
         public void onObbResult(String filename, String status) throws RemoteException {
-            Log.i(TAG, "filename = " + filename + ", result = " + status);
+            synchronized (mListeners) {
+                final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator();
+                while (iter.hasNext()) {
+                    final WeakReference<ObbListenerDelegate> ref = iter.next();
+
+                    final ObbListenerDelegate delegate = (ref == null) ? null : ref.get();
+                    if (delegate == null) {
+                        iter.remove();
+                        continue;
+                    }
+
+                    delegate.sendObbStateChanged(filename, status);
+                }
+            }
+        }
+
+        public void addListener(OnObbStateChangeListener listener) {
+            if (listener == null) {
+                return;
+            }
+
+            synchronized (mListeners) {
+                final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator();
+                while (iter.hasNext()) {
+                    final WeakReference<ObbListenerDelegate> ref = iter.next();
+
+                    final ObbListenerDelegate delegate = (ref == null) ? null : ref.get();
+                    if (delegate == null) {
+                        iter.remove();
+                        continue;
+                    }
+
+                    /*
+                     * If we're already in the listeners, we don't need to be in
+                     * there again.
+                     */
+                    if (listener.equals(delegate.getListener())) {
+                        return;
+                    }
+                }
+
+                final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
+                mListeners.add(new WeakReference<ObbListenerDelegate>(delegate));
+            }
+        }
+    }
+
+    /**
+     * Private class containing sender and receiver code for StorageEvents.
+     */
+    private class ObbListenerDelegate {
+        private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
+        private final Handler mHandler;
+
+        ObbListenerDelegate(OnObbStateChangeListener listener) {
+            mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
+            mHandler = new Handler(mTgtLooper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    final OnObbStateChangeListener listener = getListener();
+                    if (listener == null) {
+                        return;
+                    }
+
+                    StorageEvent e = (StorageEvent) msg.obj;
+
+                    if (msg.what == StorageEvent.EVENT_OBB_STATE_CHANGED) {
+                        ObbStateChangedStorageEvent ev = (ObbStateChangedStorageEvent) e;
+                        listener.onObbStateChange(ev.path, ev.state);
+                    } else {
+                        Log.e(TAG, "Unsupported event " + msg.what);
+                    }
+                }
+            };
+        }
+
+        OnObbStateChangeListener getListener() {
+            if (mObbEventListenerRef == null) {
+                return null;
+            }
+            return mObbEventListenerRef.get();
+        }
+
+        void sendObbStateChanged(String path, String state) {
+            ObbStateChangedStorageEvent e = new ObbStateChangedStorageEvent(path, state);
+            mHandler.sendMessage(e.getMessage());
+        }
+    }
+
+    /**
+     * Message sent during an OBB status change event.
+     */
+    private class ObbStateChangedStorageEvent extends StorageEvent {
+        public final String path;
+        public final String state;
+
+        public ObbStateChangedStorageEvent(String path, String state) {
+            super(EVENT_OBB_STATE_CHANGED);
+            this.path = path;
+            this.state = state;
         }
     }
 
@@ -89,8 +205,9 @@
      * and the target looper handler.
      */
     private class StorageEvent {
-        public static final int EVENT_UMS_CONNECTION_CHANGED = 1;
-        public static final int EVENT_STORAGE_STATE_CHANGED   = 2;
+        static final int EVENT_UMS_CONNECTION_CHANGED = 1;
+        static final int EVENT_STORAGE_STATE_CHANGED = 2;
+        static final int EVENT_OBB_STATE_CHANGED = 3;
 
         private Message mMessage;
 
@@ -291,19 +408,27 @@
      * specified, it is supplied to the mounting process to be used in any
      * encryption used in the OBB.
      * <p>
+     * The OBB will remain mounted for as long as the StorageManager reference
+     * is held by the application. As soon as this reference is lost, the OBBs
+     * in use will be unmounted. The {@link OnObbStateChangeListener} registered with
+     * this call will receive all further OBB-related events until it goes out
+     * of scope. If the caller is not interested in whether the call succeeds,
+     * the <code>listener</code> may be specified as <code>null</code>.
+     * <p>
      * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
      * 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
+     * That is, shared UID applications can attempt to mount 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
+     * @param key secret used to encrypt the OBB; may be <code>null</code> if no
+     *            encryption was used on the OBB.
      * @return whether the mount call was successfully queued or not
+     * @throws IllegalArgumentException when the OBB is already mounted
      */
-    public boolean mountObb(String filename, String key) {
+    public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) {
         try {
+            mObbActionListener.addListener(listener);
             mMountService.mountObb(filename, key, mObbActionListener);
             return true;
         } catch (RemoteException e) {
@@ -314,15 +439,20 @@
     }
 
     /**
-     * Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag
-     * is true, it will kill any application needed to unmount the given OBB.
+     * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
+     * <code>force</code> flag is true, it will kill any application needed to
+     * unmount the given OBB (even the calling application).
+     * <p>
+     * The {@link OnObbStateChangeListener} registered with this call will receive all
+     * further OBB-related events until it goes out of scope. If the caller is
+     * not interested in whether the call succeeded, the listener may be
+     * specified as <code>null</code>.
      * <p>
      * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
      * 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
@@ -330,8 +460,10 @@
      * @return whether the unmount call was successfully queued or not
      * @throws IllegalArgumentException when OBB is not already mounted
      */
-    public boolean unmountObb(String filename, boolean force) throws IllegalArgumentException {
+    public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener)
+            throws IllegalArgumentException {
         try {
+            mObbActionListener.addListener(listener);
             mMountService.unmountObb(filename, force, mObbActionListener);
             return true;
         } catch (RemoteException e) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 3735d32..7944807 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -4948,7 +4948,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
+        if (mNativeClass == 0 || (!isClickable() && !isLongClickable())) {
             return false;
         }
 
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index afb56fc..27f5ad4 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -122,7 +122,7 @@
 
                         // Post a runnable to call back to the view to notify it that we have
                         // connected
-                        adapter. mMainQueue.post(new Runnable() {
+                        adapter.mMainQueue.post(new Runnable() {
                             @Override
                             public void run() {
                                 final RemoteAdapterConnectionCallback callback =
@@ -148,7 +148,7 @@
             adapter.mMainQueue.removeMessages(0);
             adapter.mWorkerQueue.removeMessages(0);
 
-            // Clear the cache
+            // Clear the cache (the meta data will be re-requested on service re-connection)
             synchronized (adapter.mCache) {
                 adapter.mCache.reset();
             }
@@ -183,9 +183,13 @@
          *             successfully.
          */
         public void onRemoteViewsLoaded(RemoteViews view) {
-            // Remove all the children of this layout first
-            removeAllViews();
-            addView(view.apply(getContext(), this));
+            try {
+                // Remove all the children of this layout first
+                removeAllViews();
+                addView(view.apply(getContext(), this));
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to apply RemoteViews.");
+            }
         }
     }
 
@@ -224,6 +228,8 @@
          * the associated RemoteViews has loaded.
          */
         public void notifyOnRemoteViewsLoaded(int position, RemoteViews view, int typeId) {
+            if (view == null) return;
+
             final Integer pos = position;
             if (mReferences.containsKey(pos)) {
                 // Notify all the references for that position of the newly loaded RemoteViews
@@ -555,11 +561,14 @@
         }
 
         public void reset() {
+            // Note: We do not try and reset the meta data, since that information is still used by
+            // collection views to validate it's own contents (and will be re-requested if the data
+            // is invalidated through the notifyDataSetChanged() flow).
+
             mPreloadLowerBound = 0;
             mPreloadUpperBound = -1;
             mIndexRemoteViews.clear();
             mIndexMetaData.clear();
-            mMetaData.reset();
             synchronized (mLoadIndices) {
                 mRequestedIndices.clear();
                 mLoadIndices.clear();
@@ -834,11 +843,6 @@
     }
 
     public void notifyDataSetChanged() {
-        synchronized (mCache) {
-            // Flush the cache so that we can reload new items from the service
-            mCache.reset();
-        }
-
         final RemoteViewsMetaData metaData = mCache.getMetaData();
         synchronized (metaData) {
             // Set flag to calls the remote factory's onDataSetChanged() on the next worker loop
@@ -864,6 +868,11 @@
             }
         }
 
+        // Flush the cache so that we can reload new items from the service
+        synchronized (mCache) {
+            mCache.reset();
+        }
+
         // Re-request the new metadata (only after the notification to the factory)
         updateMetaData();
 
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index b9ded190..16126aa 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -82,27 +82,27 @@
         public RemoteViewsFactoryAdapter(RemoteViewsFactory factory) {
             mFactory = factory;
         }
-        public void onDataSetChanged() {
+        public synchronized void onDataSetChanged() {
             mFactory.onDataSetChanged();
         }
-        public int getCount() {
+        public synchronized int getCount() {
             return mFactory.getCount();
         }
-        public RemoteViews getViewAt(int position) {
+        public synchronized RemoteViews getViewAt(int position) {
             RemoteViews rv = mFactory.getViewAt(position);
             rv.setIsWidgetCollectionChild(true);
             return rv;
         }
-        public RemoteViews getLoadingView() {
+        public synchronized RemoteViews getLoadingView() {
             return mFactory.getLoadingView();
         }
-        public int getViewTypeCount() {
+        public synchronized int getViewTypeCount() {
             return mFactory.getViewTypeCount();
         }
-        public long getItemId(int position) {
+        public synchronized long getItemId(int position) {
             return mFactory.getItemId(position);
         }
-        public boolean hasStableIds() {
+        public synchronized boolean hasStableIds() {
             return mFactory.hasStableIds();
         }
 
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 09217af..dd67197 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.database.Cursor;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.text.Editable;
@@ -348,15 +349,19 @@
     }
 
     private void setImeVisibility(boolean visible) {
-        // We made sure the IME was displayed, so also make sure it is closed
-        // when we go away.
-        InputMethodManager imm = (InputMethodManager)
-                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-        if (imm != null) {
-            if (visible) {
-                imm.showSoftInputUnchecked(0, null);
-            } else {
-                imm.hideSoftInputFromWindow(getWindowToken(), 0);
+        // don't mess with the soft input if we're not iconified by default
+        if (mIconifiedByDefault) {
+            InputMethodManager imm = (InputMethodManager)
+            getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+            // We made sure the IME was displayed, so also make sure it is closed
+            // when we go away.
+            if (imm != null) {
+                if (visible) {
+                    imm.showSoftInputUnchecked(0, null);
+                } else {
+                    imm.hideSoftInputFromWindow(getWindowToken(), 0);
+                }
             }
         }
     }
@@ -717,4 +722,13 @@
         public void afterTextChanged(Editable s) {
         }
     };
-}
+
+    /*
+     * Avoid getting focus when searching for something to focus on.
+     * The user will have to touch the text view to get focus.
+     */
+    protected boolean onRequestFocusInDescendants(int direction,
+            Rect previouslyFocusedRect) {
+        return false;
+    }
+ }
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index 62c89fc..2a9eacf 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -34,7 +34,17 @@
     jfieldID flags;
 } gObbInfoClassInfo;
 
-static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
+static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
+{
+    jclass npeClazz;
+
+    npeClazz = env->FindClass(exc);
+    LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
+
+    env->ThrowNew(npeClazz, msg);
+}
+
+static void android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
         jobject obbInfo)
 {
     const char* filePath = env->GetStringUTFChars(file, JNI_FALSE);
@@ -42,7 +52,8 @@
     sp<ObbFile> obb = new ObbFile();
     if (!obb->readFrom(filePath)) {
         env->ReleaseStringUTFChars(file, filePath);
-        return JNI_FALSE;
+        doThrow(env, "java/io/IOException", "Could not read OBB file");
+        return;
     }
 
     env->ReleaseStringUTFChars(file, filePath);
@@ -51,13 +62,13 @@
 
     jstring packageName = env->NewStringUTF(packageNameStr);
     if (packageName == NULL) {
-        return JNI_FALSE;
+        doThrow(env, "java/io/IOException", "Could not read OBB file");
+        return;
     }
 
     env->SetObjectField(obbInfo, gObbInfoClassInfo.packageName, packageName);
     env->SetIntField(obbInfo, gObbInfoClassInfo.version, obb->getVersion());
-
-    return JNI_TRUE;
+    env->SetIntField(obbInfo, gObbInfoClassInfo.flags, obb->getFlags());
 }
 
 /*
@@ -65,7 +76,7 @@
  */
 static JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    { "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
+    { "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)V",
             (void*) android_content_res_ObbScanner_getObbInfo },
 };
 
diff --git a/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
similarity index 99%
rename from core/tests/coretests/src/android/net/DownloadManagerBaseTest.java
rename to core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index a7ec7d5..ed42e64 100644
--- a/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.app;
 
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.Cursor;
 import android.net.ConnectivityManager;
-import android.net.DownloadManager;
 import android.net.NetworkInfo;
-import android.net.DownloadManager.Query;
-import android.net.DownloadManager.Request;
+import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.os.Environment;
diff --git a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
similarity index 98%
rename from core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java
rename to core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
index a61f02d..38f336e 100644
--- a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.app;
 
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.app.DownloadManagerBaseTest.DataType;
+import android.app.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.Cursor;
-import android.net.DownloadManager;
-import android.net.DownloadManager.Query;
-import android.net.DownloadManager.Request;
-import android.net.DownloadManagerBaseTest.DataType;
-import android.net.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver;
+import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
diff --git a/core/tests/coretests/src/android/net/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
similarity index 97%
rename from core/tests/coretests/src/android/net/DownloadManagerStressTest.java
rename to core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index 9fa8620..4ff0295 100644
--- a/core/tests/coretests/src/android/net/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.app;
 
 import java.io.File;
 import java.util.Random;
 
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
 import android.database.Cursor;
-import android.net.DownloadManager.Query;
-import android.net.DownloadManager.Request;
+import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
index 576765c..7206f4a 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
@@ -19,7 +19,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-				   ../../../coretests/src/android/net/DownloadManagerBaseTest.java
+				   ../../../coretests/src/android/app/DownloadManagerBaseTest.java
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
 LOCAL_SDK_VERSION := current
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
index f21e7ac..c0f670b 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
@@ -15,13 +15,13 @@
  */
 package com.android.frameworks.downloadmanagertests;
 
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.app.DownloadManagerBaseTest;
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
-import android.net.DownloadManager;
-import android.net.DownloadManager.Query;
-import android.net.DownloadManager.Request;
-import android.net.DownloadManagerBaseTest;
 import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 2aa3e84..c4421c3 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -601,9 +601,9 @@
             while(mRun) {
                 rbuf[0] = 0;
                 int msg = mRS.nContextGetMessage(mRS.mContext, rbuf, true);
-                if ((msg == 0) && mRun) {
+                if ((msg == 0)) {
                     // Can happen for two reasons
-                    if (rbuf[0] > 0) {
+                    if (rbuf[0] > 0 && mRun) {
                         // 1: Buffer needs to be enlarged.
                         rbuf = new int[rbuf[0] + 2];
                     } else {
@@ -616,6 +616,7 @@
                         } catch(InterruptedException e) {
                         }
                     }
+                    continue;
                 }
                 if(mRS.mMessageCallback != null) {
                     mRS.mMessageCallback.mData = rbuf;
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 9a09586..ed2f7d7 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -27,6 +27,7 @@
 
 class MediaSource;
 class AudioTrack;
+class AwesomePlayer;
 
 class AudioPlayer : public TimeSource {
 public:
@@ -35,7 +36,9 @@
         SEEK_COMPLETE
     };
 
-    AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
+    AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink,
+                AwesomePlayer *audioObserver = NULL);
+
     virtual ~AudioPlayer();
 
     // Caller retains ownership of "source".
@@ -91,6 +94,7 @@
     MediaBuffer *mFirstBuffer;
 
     sp<MediaPlayerBase::AudioSink> mAudioSink;
+    AwesomePlayer *mObserver;
 
     static void AudioCallback(int event, void *user, void *info);
     void AudioCallback(int event, void *info);
diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h
new file mode 100644
index 0000000..551ca01
--- /dev/null
+++ b/include/media/stagefright/MPEG2TSWriter.h
@@ -0,0 +1,72 @@
+/*
+ * 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 MPEG2TS_WRITER_H_
+
+#define MPEG2TS_WRITER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/MediaWriter.h>
+
+namespace android {
+
+struct MPEG2TSWriter : public MediaWriter {
+    MPEG2TSWriter(const char *filename);
+
+    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 status_t dump(int fd, const Vector<String16>& args);
+
+    void onMessageReceived(const sp<AMessage> &msg);
+
+protected:
+    virtual ~MPEG2TSWriter();
+
+private:
+    enum {
+        kWhatSourceNotify = 'noti'
+    };
+
+    struct SourceInfo;
+
+    FILE *mFile;
+    sp<ALooper> mLooper;
+    sp<AHandlerReflector<MPEG2TSWriter> > mReflector;
+
+    bool mStarted;
+
+    Vector<sp<SourceInfo> > mSources;
+    size_t mNumSourcesDone;
+
+    int64_t mNumTSPacketsWritten;
+    int64_t mNumTSPacketsBeforeMeta;
+
+    void writeTS();
+    void writeProgramAssociationTable();
+    void writeProgramMap();
+    void writeAccessUnit(int32_t sourceIndex, const sp<ABuffer> &buffer);
+
+    DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSWriter);
+};
+
+}  // namespace android
+
+#endif  // MPEG2TS_WRITER_H_
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 1594e31..ab2f11d 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -95,6 +95,8 @@
 
     // Ogg files can be tagged to be automatically looping...
     kKeyAutoLoop          = 'autL',  // bool (int32_t)
+
+    kKeyValidSamples      = 'valD',  // int32_t
 };
 
 enum {
diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h
index a2735a4..436fc38 100644
--- a/include/storage/IMountService.h
+++ b/include/storage/IMountService.h
@@ -62,7 +62,8 @@
     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 void unmountObb(const String16& filename, const bool force,
+            const sp<IObbActionListener>& token) = 0;
     virtual bool isObbMounted(const String16& filename) = 0;
     virtual bool getMountedObbPath(const String16& filename, String16& path) = 0;
 };
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
index cd8f814..dbc9133 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
@@ -40,6 +40,8 @@
     private ScriptC_rslist mScript;
 
     private ArrayList<UnitTest> unitTests;
+    private ListIterator<UnitTest> test_iter;
+    private UnitTest activeTest;
 
     public void init(RenderScriptGL rs, Resources res, int width, int height) {
         mRS = rs;
@@ -54,9 +56,13 @@
         unitTests.add(new UT_primitives(this, mRes));
         unitTests.add(new UT_fp_mad(this, mRes));
         /*
-        unitTests.add(new UnitTest("<Pass>", 1));
+        unitTests.add(new UnitTest(null, "<Pass>", 1));
         unitTests.add(new UnitTest());
-        unitTests.add(new UnitTest("<Fail>", -1));
+        unitTests.add(new UnitTest(null, "<Fail>", -1));
+
+        for (int i = 0; i < 20; i++) {
+            unitTests.add(new UnitTest(null, "<Pass>", 1));
+        }
         */
 
         UnitTest [] uta = new UnitTest[unitTests.size()];
@@ -71,19 +77,6 @@
             uta[i].setItem(listElem);
         }
 
-        /* Run the actual unit tests */
-        ListIterator<UnitTest> test_iter = unitTests.listIterator();
-        while (test_iter.hasNext()) {
-            UnitTest t = test_iter.next();
-            t.start();
-            /*
-            try {
-                t.join();
-            } catch (InterruptedException e) {
-            }
-            */
-        }
-
         mListAllocs.copyAll();
 
         mScript.bind_gList(mListAllocs);
@@ -92,10 +85,40 @@
         mScript.set_gFont(mFont);
 
         mRS.contextBindRootScript(mScript);
-        mRS.finish();
+
+        test_iter = unitTests.listIterator();
+        refreshTestResults(); /* Kick off the first test */
+    }
+
+    static int count = 0;
+    public void checkAndRunNextTest() {
+        if (activeTest != null) {
+            if (!activeTest.isAlive()) {
+                /* Properly clean up on our last test */
+                try {
+                    activeTest.join();
+                }
+                catch (InterruptedException e) {
+                }
+                activeTest = null;
+            }
+        }
+
+        if (activeTest == null) {
+            if (test_iter.hasNext()) {
+                activeTest = test_iter.next();
+                activeTest.start();
+                /* This routine will only get called once when a new test
+                 * should start running. The message handler in UnitTest.java
+                 * ensures this. */
+            }
+        }
+        count++;
     }
 
     public void refreshTestResults() {
+        checkAndRunNextTest();
+
         if (mListAllocs != null && mScript != null && mRS != null) {
             mListAllocs.copyAll();
 
@@ -111,6 +134,7 @@
         mScript.set_gDY(0.0f);
         mLastX = x;
         mLastY = y;
+        refreshTestResults();
     }
 
     public void onActionMove(int x, int y) {
@@ -125,5 +149,6 @@
 
         mLastX = x;
         mLastY = y;
+        refreshTestResults();
     }
 }
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
index d98b763..5eb0d67 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
@@ -16,6 +16,7 @@
 
 package com.android.rs.test;
 import android.renderscript.RenderScript.RSMessage;
+import android.util.Log;
 
 public class UnitTest extends Thread {
     public String name;
@@ -27,11 +28,15 @@
     public static final int RS_MSG_TEST_PASSED = 100;
     public static final int RS_MSG_TEST_FAILED = 101;
 
+    private static int numTests = 0;
+    public int testID;
+
     protected UnitTest(RSTestCore rstc, String n, int initResult) {
         super();
         mRSTC = rstc;
         name = n;
         result = initResult;
+        testID = numTests++;
     }
 
     protected UnitTest(RSTestCore rstc, String n) {
@@ -56,12 +61,20 @@
                     result = -1;
                     break;
                 default:
-                    break;
+                    android.util.Log.v("RenderScript", "Unit test got unexpected message");
+                    return;
             }
 
             if (mItem != null) {
                 mItem.result = result;
-                mRSTC.refreshTestResults();
+                try {
+                    mRSTC.refreshTestResults();
+                }
+                catch (IllegalStateException e) {
+                    /* Ignore the case where our message receiver has been
+                       disconnected. This happens when we leave the application
+                       before it finishes running all of the unit tests. */
+                }
             }
         }
     };
@@ -72,6 +85,9 @@
 
     public void run() {
         /* This method needs to be implemented for each subclass */
+        if (mRSTC != null) {
+            mRSTC.refreshTestResults();
+        }
     }
 }
 
diff --git a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
index eb82e56..dfd77e6 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
+++ b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
@@ -170,7 +170,7 @@
 
     // TODO Actually verify test result accuracy
     rsDebug("fp_mad_test PASSED", 0);
-    rsSendToClient(RS_MSG_TEST_PASSED);
+    rsSendToClientBlocking(RS_MSG_TEST_PASSED);
 }
 
 
diff --git a/libs/rs/java/tests/src/com/android/rs/test/primitives.rs b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
index 2ba5d52..5312bcc 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
+++ b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
@@ -42,10 +42,10 @@
     failed |= test_primitive_types(index);
 
     if (failed) {
-        rsSendToClient(RS_MSG_TEST_FAILED);
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
     }
     else {
-        rsSendToClient(RS_MSG_TEST_PASSED);
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
     }
 }
 
diff --git a/libs/rs/java/tests/src/com/android/rs/test/rslist.rs b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs
index 72d1850..a5f0f6b 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/rslist.rs
+++ b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs
@@ -56,6 +56,27 @@
     int height = rsgGetHeight();
 
     int itemHeight = 80;
+    int totalItemHeight = itemHeight * allocSize;
+
+    /* Prevent scrolling above the top of the list */
+    int firstItem = height - totalItemHeight;
+    if (firstItem < 0) {
+        firstItem = 0;
+    }
+
+    /* Prevent scrolling past the last line of the list */
+    int lastItem = -1 * (totalItemHeight - height);
+    if (lastItem > 0) {
+        lastItem = 0;
+    }
+
+    if (textPos > firstItem) {
+        textPos = firstItem;
+    }
+    else if (textPos < lastItem) {
+        textPos = lastItem;
+    }
+
     int currentYPos = itemHeight + textPos;
 
     for(int i = 0; i < allocSize; i ++) {
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 3681bc2..a7f380f 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -33,6 +33,8 @@
 #include <GLES2/gl2ext.h>
 
 #include <cutils/sched_policy.h>
+#include <sys/syscall.h>
+#include <string.h>
 
 using namespace android;
 using namespace android::renderscript;
@@ -371,11 +373,15 @@
      rsc->mWorkers.mLaunchSignals[idx].init();
      rsc->mWorkers.mNativeThreadId[idx] = gettid();
 
-     //cpu_set_t cpset[16];
-     //int ret = sched_getaffinity(rsc->mWorkers.mNativeThreadId[idx], sizeof(cpset), &cpset);
-     //LOGE("ret = %i", ret);
-
-//sched_setaffinity
+#if 0
+     typedef struct {uint64_t bits[1024 / 64]; } cpu_set_t;
+     cpu_set_t cpuset;
+     memset(&cpuset, 0, sizeof(cpuset));
+     cpuset.bits[idx / 64] |= 1ULL << (idx % 64);
+     int ret = syscall(241, rsc->mWorkers.mNativeThreadId[idx],
+		       sizeof(cpuset), &cpuset);
+     LOGE("SETAFFINITY ret = %i %s", ret, EGLUtils::strerror(ret));
+#endif
 
      setpriority(PRIO_PROCESS, rsc->mWorkers.mNativeThreadId[idx], rsc->mThreadPriority);
      while(rsc->mRunning) {
@@ -490,6 +496,7 @@
         usleep(100);
     }
 
+    mWorkers.mCompleteSignal.init();
     mWorkers.mRunningCount = 0;
     mWorkers.mLaunchCount = 0;
     for (uint32_t ct=0; ct < mWorkers.mCount; ct++) {
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index bce9c13..b85d2a8 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -174,7 +174,7 @@
     uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;}
 
     void launchThreads(WorkerCallback_t cbk, void *data);
-    uint32_t getWorkerPoolSize() const {return (uint32_t)mWorkers.mRunningCount;}
+    uint32_t getWorkerPoolSize() const {return (uint32_t)mWorkers.mCount;}
 
 protected:
     Device *mDev;
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index f905492..e9621b9 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -201,7 +201,7 @@
         }
 
         //LOGE("usr idx %i, x %i,%i  y %i,%i", idx, mtls->xStart, mtls->xEnd, yStart, yEnd);
-
+        //LOGE("usr ptr in %p,  out %p", mtls->ptrIn, mtls->ptrOut);
         for (uint32_t y = yStart; y < yEnd; y++) {
             uint32_t offset = mtls->dimX * y;
             uint8_t *xPtrOut = mtls->ptrOut + (mtls->eStrideOut * offset);
@@ -296,14 +296,12 @@
         mtls.eStrideOut = aout->getType()->getElementSizeBytes();
     }
 
-
-    if ((rsc->getWorkerPoolSize() > 1) && mEnviroment.mIsThreadable &&
-        ((mtls.dimY * mtls.dimZ * mtls.dimArray) > 1)) {
+    if ((rsc->getWorkerPoolSize() > 1) && mEnviroment.mIsThreadable && (mtls.dimY > 1)) {
 
         //LOGE("launch 1");
         rsc->launchThreads(wc_xy, &mtls);
-        //LOGE("launch 2");
     } else {
+        //LOGE("launch 3");
         for (uint32_t ar = mtls.arrayStart; ar < mtls.arrayEnd; ar++) {
             for (uint32_t z = mtls.zStart; z < mtls.zEnd; z++) {
                 for (uint32_t y = mtls.yStart; y < mtls.yEnd; y++) {
@@ -380,11 +378,11 @@
     if (sym) {
         return sym->mPtr;
     }
-    s->mEnviroment.mIsThreadable = false;
     sym = ScriptCState::lookupSymbolCL(name);
     if (sym) {
         return sym->mPtr;
     }
+    s->mEnviroment.mIsThreadable = false;
     sym = ScriptCState::lookupSymbolGL(name);
     if (sym) {
         return sym->mPtr;
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 902bb27..3ad9319 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -429,8 +429,8 @@
         reply.readExceptionCode();
     }
 
-    void mountObb(const String16& filename, const String16& key, const sp<
-            IObbActionListener>& token)
+    void mountObb(const String16& filename, const String16& key,
+            const sp<IObbActionListener>& token)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
@@ -448,7 +448,7 @@
         }
     }
 
-    void unmountObb(const String16& filename, const bool force)
+    void unmountObb(const String16& filename, const bool force, const sp<IObbActionListener>& token)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 8db8c42..16ce24b 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -1070,7 +1070,8 @@
         }
 
         // Figure out whether splitting will be allowed for this window.
-        if (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH) {
+        if (newTouchedWindow
+                && (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH)) {
             // New window supports splitting.
             isSplit = true;
         } else if (isSplit) {
diff --git a/location/Android.mk b/location/Android.mk
new file mode 100644
index 0000000..12db2f7
--- /dev/null
+++ b/location/Android.mk
@@ -0,0 +1,19 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(TARGET_BUILD_APPS),)
+include $(call all-makefiles-under, $(LOCAL_PATH))
+endif
diff --git a/location/lib/Android.mk b/location/lib/Android.mk
new file mode 100644
index 0000000..a06478a
--- /dev/null
+++ b/location/lib/Android.mk
@@ -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.
+#
+LOCAL_PATH := $(call my-dir)
+
+# the library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= com.android.location.provider
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+            $(call all-subdir-java-files)
+
+include $(BUILD_JAVA_LIBRARY)
+
+
+# ====  com.google.location.xml lib def  ========================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := com.android.location.provider.xml
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_CLASS := ETC
+
+# This will install the file in /system/etc/permissions
+#
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
+
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+
+include $(BUILD_PREBUILT)
diff --git a/location/lib/com.android.location.provider.xml b/location/lib/com.android.location.provider.xml
new file mode 100644
index 0000000..000e68f
--- /dev/null
+++ b/location/lib/com.android.location.provider.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.
+-->
+
+<permissions>
+    <library name="com.android.location.provider"
+            file="/system/framework/com.android.location.provider.jar" />
+</permissions>
diff --git a/location/java/android/location/provider/GeocodeProvider.java b/location/lib/java/com/android/location/provider/GeocodeProvider.java
similarity index 98%
rename from location/java/android/location/provider/GeocodeProvider.java
rename to location/lib/java/com/android/location/provider/GeocodeProvider.java
index 493c631..666bb02 100644
--- a/location/java/android/location/provider/GeocodeProvider.java
+++ b/location/lib/java/com/android/location/provider/GeocodeProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.location.provider;
+package com.android.location.provider;
 
 import android.os.IBinder;
 
@@ -80,4 +80,3 @@
         return mProvider;
     }
 }
-
diff --git a/location/java/android/location/provider/LocationProvider.java b/location/lib/java/com/android/location/provider/LocationProvider.java
similarity index 99%
rename from location/java/android/location/provider/LocationProvider.java
rename to location/lib/java/com/android/location/provider/LocationProvider.java
index 14dea14..3714f40 100644
--- a/location/java/android/location/provider/LocationProvider.java
+++ b/location/lib/java/com/android/location/provider/LocationProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.location.provider;
+package com.android.location.provider;
 
 import android.content.Context;
 import android.net.NetworkInfo;
@@ -356,4 +356,3 @@
      */
     public abstract void onRemoveListener(int uid, WorkSource ws);
 }
-
diff --git a/media/java/android/media/videoeditor/Effect.java b/media/java/android/media/videoeditor/Effect.java
index 635727a..038bfc2 100755
--- a/media/java/android/media/videoeditor/Effect.java
+++ b/media/java/android/media/videoeditor/Effect.java
@@ -57,6 +57,10 @@
             throw new IllegalArgumentException("Media item cannot be null");

         }

 

+        if (startTimeMs + durationMs > mediaItem.getTimelineDuration()) {

+            throw new IllegalArgumentException("Invalid start time and duration");

+        }

+

         mMediaItem = mediaItem;

         mUniqueId = effectId;

         mStartTimeMs = startTimeMs;

diff --git a/media/java/android/media/videoeditor/Overlay.java b/media/java/android/media/videoeditor/Overlay.java
index 8e20651..d9e7f85 100755
--- a/media/java/android/media/videoeditor/Overlay.java
+++ b/media/java/android/media/videoeditor/Overlay.java
@@ -16,6 +16,9 @@
 

 package android.media.videoeditor;

 

+import java.util.HashMap;

+import java.util.Map;

+

 

 /**

  * This is the super class for all Overlay classes.

@@ -26,6 +29,8 @@
     private final String mUniqueId;

     // The overlay owner

     private final MediaItem mMediaItem;

+    // user attributes

+    private final Map<String, String> mUserAttributes;

 

     protected long mStartTimeMs;

     protected long mDurationMs;

@@ -36,10 +41,7 @@
      */

     @SuppressWarnings("unused")

     private Overlay() {

-        mUniqueId = null;

-        mMediaItem = null;

-        mStartTimeMs = 0;

-        mDurationMs = 0;

+        this(null, null, 0, 0);

     }

 

     /**

@@ -58,10 +60,15 @@
             throw new IllegalArgumentException("Media item cannot be null");

         }

 

+        if (startTimeMs + durationMs > mediaItem.getTimelineDuration()) {

+            throw new IllegalArgumentException("Invalid start time and duration");

+        }

+

         mMediaItem = mediaItem;

         mUniqueId = overlayId;

         mStartTimeMs = startTimeMs;

         mDurationMs = durationMs;

+        mUserAttributes = new HashMap<String, String>();

     }

 

     /**

@@ -125,6 +132,23 @@
         return mMediaItem;

     }

 

+    /**

+     * Set a user attribute

+     *

+     * @param name The attribute name

+     * @param value The attribute value

+     */

+    public void setUserAttribute(String name, String value) {

+        mUserAttributes.put(name, value);

+    }

+

+    /**

+     * @return The user attributes

+     */

+    public Map<String, String> getUserAttributes() {

+        return mUserAttributes;

+    }

+

     /*

      * {@inheritDoc}

      */

diff --git a/media/java/android/media/videoeditor/OverlayFrame.java b/media/java/android/media/videoeditor/OverlayFrame.java
index 91deee4..c7dfc39 100755
--- a/media/java/android/media/videoeditor/OverlayFrame.java
+++ b/media/java/android/media/videoeditor/OverlayFrame.java
@@ -32,7 +32,7 @@
  */

 public class OverlayFrame extends Overlay {

     // Instance variables

-    private final Bitmap mBitmap;

+    private Bitmap mBitmap;

     private String mFilename;

 

     /**

@@ -93,6 +93,22 @@
     }

 

     /**

+     * @param bitmap The overlay bitmap

+     */

+    public void setBitmap(Bitmap bitmap) {

+        mBitmap = bitmap;

+        if (mFilename != null) {

+            // Delete the file

+            new File(mFilename).delete();

+            // Invalidate the filename

+            mFilename = null;

+        }

+

+        // Invalidate the transitions if necessary

+        getMediaItem().invalidateTransitions(this);

+    }

+

+    /**

      * Get the file name of this overlay

      */

     String getFilename() {

diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
index 0861992..5df4ea5 100644
--- a/media/java/android/media/videoeditor/VideoEditorTestImpl.java
+++ b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
@@ -25,6 +25,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -50,6 +51,10 @@
     private static final String TAG_MEDIA_ITEM = "media_item";
     private static final String TAG_TRANSITIONS = "transitions";
     private static final String TAG_TRANSITION = "transition";
+    private static final String TAG_OVERLAYS = "overlays";
+    private static final String TAG_OVERLAY = "overlay";
+    private static final String TAG_OVERLAY_USER_ATTRIBUTES = "overlay_user_attributes";
+
     private static final String ATTR_ID = "id";
     private static final String ATTR_FILENAME = "filename";
     private static final String ATTR_AUDIO_WAVEFORM_FILENAME = "wavefoem";
@@ -572,6 +577,40 @@
                         Long.toString(mediaItem.getTimelineDuration()));
             }
 
+            final List<Overlay> overlays = mediaItem.getAllOverlays();
+            if (overlays.size() > 0) {
+                serializer.startTag("", TAG_OVERLAYS);
+                for (Overlay overlay : overlays) {
+                    serializer.startTag("", TAG_OVERLAY);
+                    serializer.attribute("", ATTR_ID, overlay.getId());
+                    serializer.attribute("", ATTR_TYPE, overlay.getClass().getSimpleName());
+                    serializer.attribute("", ATTR_BEGIN_TIME,
+                            Long.toString(overlay.getStartTime()));
+                    serializer.attribute("", ATTR_DURATION, Long.toString(overlay.getDuration()));
+                    if (overlay instanceof OverlayFrame) {
+                        final OverlayFrame overlayFrame = (OverlayFrame)overlay;
+                        overlayFrame.save(this);
+                        if (overlayFrame.getFilename() != null) {
+                            serializer.attribute("", ATTR_FILENAME, overlayFrame.getFilename());
+                        }
+                    }
+
+                    // Save the user attributes
+                    serializer.startTag("", TAG_OVERLAY_USER_ATTRIBUTES);
+                    final Map<String, String> userAttributes = overlay.getUserAttributes();
+                    for (String name : userAttributes.keySet()) {
+                        final String value = userAttributes.get(name);
+                        if (value != null) {
+                            serializer.attribute("", name, value);
+                        }
+                    }
+                    serializer.endTag("", TAG_OVERLAY_USER_ATTRIBUTES);
+
+                    serializer.endTag("", TAG_OVERLAY);
+                }
+                serializer.endTag("", TAG_OVERLAYS);
+            }
+
             serializer.endTag("", TAG_MEDIA_ITEM);
         }
         serializer.endTag("", TAG_MEDIA_ITEMS);
@@ -629,21 +668,22 @@
         parser.setInput(new FileInputStream(file), "UTF-8");
         int eventType = parser.getEventType();
         String name;
+        MediaItem currentMediaItem = null;
+        Overlay currentOverlay = null;
         while (eventType != XmlPullParser.END_DOCUMENT) {
             switch (eventType) {
                 case XmlPullParser.START_TAG: {
                     name = parser.getName();
-                    if (name.equals(TAG_PROJECT)) {
+                    if (TAG_PROJECT.equals(name)) {
                         mAspectRatio = Integer.parseInt(parser.getAttributeValue("",
                                 ATTR_ASPECT_RATIO));
-                    } else if (name.equals(TAG_MEDIA_ITEM)) {
+                    } else if (TAG_MEDIA_ITEM.equals(name)) {
                         final String mediaItemId = parser.getAttributeValue("", ATTR_ID);
                         final String type = parser.getAttributeValue("", ATTR_TYPE);
                         final String filename = parser.getAttributeValue("", ATTR_FILENAME);
                         final int renderingMode = Integer.parseInt(parser.getAttributeValue("",
                                 ATTR_RENDERING_MODE));
 
-                        MediaItem currentMediaItem;
                         if (MediaImageItem.class.getSimpleName().equals(type)) {
                             final long durationMs = Long.parseLong(parser.getAttributeValue("",
                                     ATTR_DURATION));
@@ -673,11 +713,36 @@
                         if (currentMediaItem != null) {
                             mMediaItems.add(currentMediaItem);
                         }
-                    } else if (name.equals(TAG_TRANSITION)) {
+                    } else if (TAG_TRANSITION.equals(name)) {
                         final Transition transition = parseTransition(parser);
                         if (transition != null) {
                             mTransitions.add(transition);
                         }
+                    } else if (TAG_OVERLAY.equals(name)) {
+                        if (currentMediaItem != null) {
+                            currentOverlay = parseOverlay(parser, currentMediaItem);
+                            if (currentOverlay != null) {
+                                currentMediaItem.addOverlay(currentOverlay);
+                            }
+                        }
+                    } else if (TAG_OVERLAY_USER_ATTRIBUTES.equals(name)) {
+                        if (currentOverlay != null) {
+                            final int attributesCount = parser.getAttributeCount();
+                            for (int i = 0; i < attributesCount; i++) {
+                                currentOverlay.setUserAttribute(parser.getAttributeName(i),
+                                        parser.getAttributeValue(i));
+                            }
+                        }
+                    }
+                    break;
+                }
+
+                case XmlPullParser.END_TAG: {
+                    name = parser.getName();
+                    if (TAG_MEDIA_ITEM.equals(name)) {
+                        currentMediaItem = null;
+                    } else if (TAG_OVERLAY.equals(name)) {
+                        currentOverlay = null;
                     }
                     break;
                 }
@@ -764,6 +829,31 @@
         return transition;
     }
 
+    /**
+     * Parse the overlay
+     *
+     * @param parser The parser
+     * @param mediaItem The media item owner
+     *
+     * @return The overlay
+     */
+    private Overlay parseOverlay(XmlPullParser parser, MediaItem mediaItem) {
+        final String overlayId = parser.getAttributeValue("", ATTR_ID);
+        final String type = parser.getAttributeValue("", ATTR_TYPE);
+        final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION));
+        final long startTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME));
+
+        final Overlay overlay;
+        if (OverlayFrame.class.getSimpleName().equals(type)) {
+            final String filename = parser.getAttributeValue("", ATTR_FILENAME);
+            overlay = new OverlayFrame(mediaItem, overlayId, filename, startTimeMs, durationMs);
+        } else {
+            overlay = null;
+        }
+
+        return overlay;
+    }
+
     public void cancelExport(String filename) {
     }
 
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 8f40130..ebe3302 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -63,7 +63,6 @@
 
 // Flag to allow a one time init of global memory, only happens on first call ever
 int LvmInitFlag = LVM_FALSE;
-int LvmSessionsActive = 0;
 SessionContext GlobalSessionMemory[LVM_MAX_SESSIONS];
 
 int SessionIndex[LVM_MAX_SESSIONS];
@@ -189,16 +188,19 @@
                             int32_t             sessionId,
                             int32_t             ioId,
                             effect_interface_t  *pInterface){
-    int ret;
+    int ret = 0;
     int sessionNo;
     int i;
-    EffectContext *pContext = new EffectContext;
+    EffectContext *pContext = NULL;
+    bool newBundle = false;
+    SessionContext *pSessionContext;
 
     LOGV("\n\tEffectCreate start session %d", sessionId);
 
     if (pInterface == NULL || uuid == NULL){
         LOGV("\tLVM_ERROR : EffectCreate() called with NULL pointer");
-        return -EINVAL;
+        ret = -EINVAL;
+        goto exit;
     }
 
     if(LvmInitFlag == LVM_FALSE){
@@ -207,8 +209,6 @@
         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] == LVM_UNUSED_SESSION)||(SessionIndex[i] == sessionId)){
@@ -221,23 +221,20 @@
 
     if(i==LVM_MAX_SESSIONS){
         LOGV("\tLVM_ERROR : Cannot find memory to allocate for current session");
-        return -EINVAL;
+        ret = -EINVAL;
+        goto exit;
     }
+
+    pContext = new EffectContext;
+
     // If this is the first create in this session
     if(GlobalSessionMemory[sessionNo].bBundledEffectsEnabled == LVM_FALSE){
         LOGV("\tEffectCreate - This is the first effect in current sessionId %d sessionNo %d",
                 sessionId, sessionNo);
 
-        LvmSessionsActive++;
-
-        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;
+        newBundle = true;
 
         pContext->pBundledContext = GlobalSessionMemory[sessionNo].pBundledContext;
         pContext->pBundledContext->SessionNo                = sessionNo;
@@ -251,17 +248,16 @@
         pContext->pBundledContext->bVirtualizerTempDisabled = LVM_FALSE;
         pContext->pBundledContext->NumberEffectsEnabled     = 0;
         pContext->pBundledContext->NumberEffectsCalled      = 0;
-        pContext->pBundledContext->frameCount               = 0;
         pContext->pBundledContext->firstVolume              = LVM_TRUE;
 
         #ifdef LVM_PCM
-
         char fileName[256];
         snprintf(fileName, 256, "/data/tmp/bundle_%p_pcm_in.pcm", pContext->pBundledContext);
         pContext->pBundledContext->PcmInPtr = fopen(fileName, "w");
         if (pContext->pBundledContext->PcmInPtr == NULL) {
             LOGV("cannot open %s", fileName);
-           return -EINVAL;
+            ret = -EINVAL;
+            goto exit;
         }
 
         snprintf(fileName, 256, "/data/tmp/bundle_%p_pcm_out.pcm", pContext->pBundledContext);
@@ -270,7 +266,8 @@
             LOGV("cannot open %s", fileName);
             fclose(pContext->pBundledContext->PcmInPtr);
            pContext->pBundledContext->PcmInPtr = NULL;
-           return -EINVAL;
+           ret = -EINVAL;
+           goto exit;
         }
         #endif
 
@@ -285,15 +282,18 @@
         pContext->pBundledContext->bMuteEnabled             = LVM_FALSE;
         pContext->pBundledContext->bStereoPositionEnabled   = LVM_FALSE;
         pContext->pBundledContext->positionSaved            = 0;
+        pContext->pBundledContext->workBuffer               = NULL;
+        pContext->pBundledContext->frameCount               = -1;
+        pContext->pBundledContext->SamplesToExitCountVirt   = 0;
+        pContext->pBundledContext->SamplesToExitCountBb     = 0;
+        pContext->pBundledContext->SamplesToExitCountEq     = 0;
 
         LOGV("\tEffectCreate - Calling LvmBundle_init");
         ret = LvmBundle_init(pContext);
 
         if (ret < 0){
             LOGV("\tLVM_ERROR : EffectCreate() Bundle init failed");
-            delete pContext->pBundledContext;
-            delete pContext;
-            return ret;
+            goto exit;
         }
     }
     else{
@@ -304,13 +304,14 @@
     }
     LOGV("\tEffectCreate - pBundledContext is %p", pContext->pBundledContext);
 
-    SessionContext *pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo];
+    pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo];
 
     // 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");
         pSessionContext->bBassInstantiated = LVM_TRUE;
+        pContext->pBundledContext->SamplesToExitCountBb = 0;
 
         pContext->itfe       = &gLvmEffectInterface;
         pContext->EffectType = LVM_BASS_BOOST;
@@ -318,6 +319,7 @@
         // Create Virtualizer
         LOGV("\tEffectCreate - Effect to be created is LVM_VIRTUALIZER");
         pSessionContext->bVirtualizerInstantiated=LVM_TRUE;
+        pContext->pBundledContext->SamplesToExitCountVirt = 0;
 
         pContext->itfe       = &gLvmEffectInterface;
         pContext->EffectType = LVM_VIRTUALIZER;
@@ -325,6 +327,7 @@
         // Create Equalizer
         LOGV("\tEffectCreate - Effect to be created is LVM_EQUALIZER");
         pSessionContext->bEqualizerInstantiated = LVM_TRUE;
+        pContext->pBundledContext->SamplesToExitCountEq = 0;
 
         pContext->itfe       = &gLvmEffectInterface;
         pContext->EffectType = LVM_EQUALIZER;
@@ -338,46 +341,77 @@
     }
     else{
         LOGV("\tLVM_ERROR : EffectCreate() invalid UUID");
-        return -EINVAL;
+        ret = -EINVAL;
+        goto exit;
     }
 
-    *pInterface = (effect_interface_t)pContext;
+exit:
+    if (ret != 0) {
+        if (pContext != NULL) {
+            if (newBundle) {
+                GlobalSessionMemory[sessionNo].bBundledEffectsEnabled = LVM_FALSE;
+                SessionIndex[sessionNo] = LVM_UNUSED_SESSION;
+                delete pContext->pBundledContext;
+            }
+            delete pContext;
+        }
+        *pInterface = (effect_interface_t)NULL;
+    } else {
+        *pInterface = (effect_interface_t)pContext;
+    }
     LOGV("\tEffectCreate end..\n\n");
-    return 0;
+    return ret;
 } /* end EffectCreate */
 
 extern "C" int EffectRelease(effect_interface_t interface){
     LOGV("\n\tEffectRelease start %p", interface);
     EffectContext * pContext = (EffectContext *)interface;
 
-    LOGV("\n\tEffectRelease start interface: %p, context %p", interface, pContext->pBundledContext);
+    LOGV("\tEffectRelease start interface: %p, context %p", interface, pContext->pBundledContext);
     if (pContext == NULL){
         LOGV("\tLVM_ERROR : EffectRelease called with NULL pointer");
         return -EINVAL;
     }
 
-
-    Effect_setEnabled(pContext, LVM_FALSE);
-
     SessionContext *pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo];
 
     // Clear the instantiated flag for the effect
+    // protect agains the case where an effect is un-instantiated without being disabled
     if(pContext->EffectType == LVM_BASS_BOOST) {
         LOGV("\tEffectRelease LVM_BASS_BOOST Clearing global intstantiated flag");
         pSessionContext->bBassInstantiated = LVM_FALSE;
+        if(pContext->pBundledContext->SamplesToExitCountBb > 0){
+            pContext->pBundledContext->NumberEffectsEnabled--;
+        }
+        pContext->pBundledContext->SamplesToExitCountBb = 0;
     } else if(pContext->EffectType == LVM_VIRTUALIZER) {
         LOGV("\tEffectRelease LVM_VIRTUALIZER Clearing global intstantiated flag");
         pSessionContext->bVirtualizerInstantiated = LVM_FALSE;
+        if(pContext->pBundledContext->SamplesToExitCountVirt > 0){
+            pContext->pBundledContext->NumberEffectsEnabled--;
+        }
+        pContext->pBundledContext->SamplesToExitCountVirt = 0;
     } else if(pContext->EffectType == LVM_EQUALIZER) {
         LOGV("\tEffectRelease LVM_EQUALIZER Clearing global intstantiated flag");
         pSessionContext->bEqualizerInstantiated =LVM_FALSE;
+        if(pContext->pBundledContext->SamplesToExitCountEq > 0){
+            pContext->pBundledContext->NumberEffectsEnabled--;
+        }
+        pContext->pBundledContext->SamplesToExitCountEq = 0;
     } else if(pContext->EffectType == LVM_VOLUME) {
         LOGV("\tEffectRelease LVM_VOLUME Clearing global intstantiated flag");
         pSessionContext->bVolumeInstantiated = LVM_FALSE;
+        if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE){
+            pContext->pBundledContext->NumberEffectsEnabled--;
+        }
     } else {
         LOGV("\tLVM_ERROR : EffectRelease : Unsupported effect\n\n\n\n\n\n\n");
     }
 
+    // Disable effect, in this case ignore errors (return codes)
+    // if an effect has already been disabled
+    Effect_setEnabled(pContext, LVM_FALSE);
+
     // if all effects are no longer instantiaed free the lvm memory and delete BundledEffectContext
     if ((pSessionContext->bBassInstantiated == LVM_FALSE) &&
             (pSessionContext->bVolumeInstantiated == LVM_FALSE) &&
@@ -395,8 +429,6 @@
         }
         #endif
 
-        LvmSessionsActive--;
-        LOGV("\tEffectRelease: There are %d LVM sessions remaining\n", LvmSessionsActive);
 
         // Clear the SessionIndex
         for(int i=0; i<LVM_MAX_SESSIONS; i++){
@@ -409,11 +441,14 @@
         }
 
         LOGV("\tEffectRelease: All effects are no longer instantiated\n");
-        pSessionContext->bBundledEffectsEnabled =LVM_FALSE;
+        pSessionContext->bBundledEffectsEnabled = LVM_FALSE;
         pSessionContext->pBundledContext = LVM_NULL;
         LOGV("\tEffectRelease: Freeing LVM Bundle memory\n");
         LvmEffect_free(pContext);
         LOGV("\tEffectRelease: Deleting LVM Bundle context %p\n", pContext->pBundledContext);
+        if (pContext->pBundledContext->workBuffer != NULL) {
+            free(pContext->pBundledContext->workBuffer);
+        }
         delete pContext->pBundledContext;
         pContext->pBundledContext = LVM_NULL;
     }
@@ -643,6 +678,14 @@
     return 0;
 }   /* end LvmBundle_init */
 
+
+static inline int16_t clamp16(int32_t sample)
+{
+    if ((sample>>15) ^ (sample>>31))
+        sample = 0x7FFF ^ (sample>>31);
+    return sample;
+}
+
 //----------------------------------------------------------------------------
 // LvmBundle_process()
 //----------------------------------------------------------------------------
@@ -668,39 +711,25 @@
 
     LVM_ControlParams_t     ActiveParams;                           /* Current control Parameters */
     LVM_ReturnStatus_en     LvmStatus = LVM_SUCCESS;                /* Function call status */
-
     LVM_INT16               *pOutTmp;
+
     if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE){
         pOutTmp = pOut;
     }else if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
-        pOutTmp = (LVM_INT16 *)malloc(frameCount * sizeof(LVM_INT16) * 2);
-        if(pOutTmp == NULL){
-            LOGV("\tLVM_ERROR : LvmBundle_process failed to allocate memory for "
-            "EFFECT_BUFFER_ACCESS_ACCUMULATE mode");
-            return -EINVAL;
+        if (pContext->pBundledContext->frameCount != frameCount) {
+            if (pContext->pBundledContext->workBuffer != NULL) {
+                free(pContext->pBundledContext->workBuffer);
+            }
+            pContext->pBundledContext->workBuffer =
+                    (LVM_INT16 *)malloc(frameCount * sizeof(LVM_INT16) * 2);
+            pContext->pBundledContext->frameCount = frameCount;
         }
+        pOutTmp = pContext->pBundledContext->workBuffer;
     }else{
         LOGV("LVM_ERROR : LvmBundle_process invalid access mode");
         return -EINVAL;
     }
 
-    /* Get the current settings */
-    LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance,
-                                         &ActiveParams);
-
-    LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "LvmBundle_process")
-    if(LvmStatus != LVM_SUCCESS) return -EINVAL;
-
-    pContext->pBundledContext->frameCount++;
-    if(pContext->pBundledContext->frameCount == 100)
-    {
-        //LOGV("\tBB: %d VIRT: %d EQ: %d, session (%d), context is %p\n",
-        //ActiveParams.BE_OperatingMode,
-        //ActiveParams.VirtualizerOperatingMode, ActiveParams.EQNB_OperatingMode,
-        //pContext->pBundledContext->SessionNo, pContext->pBundledContext);
-        pContext->pBundledContext->frameCount = 0;
-    }
-
     #ifdef LVM_PCM
     fwrite(pIn, frameCount*sizeof(LVM_INT16)*2, 1, pContext->pBundledContext->PcmInPtr);
     fflush(pContext->pBundledContext->PcmInPtr);
@@ -725,9 +754,8 @@
 
     if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
         for (int i=0; i<frameCount*2; i++){
-            pOut[i] +=  pOutTmp[i];
+            pOut[i] = clamp16((LVM_INT32)pOut[i] + (LVM_INT32)pOutTmp[i]);
         }
-        free(pOutTmp);
     }
     return 0;
 }    /* end LvmBundle_process */
@@ -813,15 +841,15 @@
         ActiveParams.BE_OperatingMode       = LVM_BE_OFF;
     }
     if(pContext->EffectType == LVM_VIRTUALIZER) {
-        LOGV("\tLvmEffect_disable : Enabling LVM_VIRTUALIZER");
+        LOGV("\tLvmEffect_disable : Disabling LVM_VIRTUALIZER");
         ActiveParams.VirtualizerOperatingMode   = LVM_MODE_OFF;
     }
     if(pContext->EffectType == LVM_EQUALIZER) {
-        LOGV("\tLvmEffect_disable : Enabling LVM_EQUALIZER");
+        LOGV("\tLvmEffect_disable : Disabling LVM_EQUALIZER");
         ActiveParams.EQNB_OperatingMode     = LVM_EQNB_OFF;
     }
     if(pContext->EffectType == LVM_VOLUME) {
-        LOGV("\tLvmEffect_disable : Enabling LVM_VOLUME");
+        LOGV("\tLvmEffect_disable : Disabling LVM_VOLUME");
     }
 
     LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
@@ -2406,85 +2434,114 @@
         switch (pContext->EffectType) {
             case LVM_BASS_BOOST:
                 if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) {
-                     LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_BASS_BOOST is already enabled");
+                     LOGV("\tEffect_setEnabled() LVM_BASS_BOOST is already enabled");
                      return -EINVAL;
                 }
+                if(pContext->pBundledContext->SamplesToExitCountBb <= 0){
+                    pContext->pBundledContext->NumberEffectsEnabled++;
+                }
                 pContext->pBundledContext->SamplesToExitCountBb =
                      (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
                 pContext->pBundledContext->bBassEnabled = LVM_TRUE;
                 break;
             case LVM_EQUALIZER:
                 if (pContext->pBundledContext->bEqualizerEnabled == LVM_TRUE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_EQUALIZER is already enabled");
+                    LOGV("\tEffect_setEnabled() LVM_EQUALIZER is already enabled");
                     return -EINVAL;
                 }
+                if(pContext->pBundledContext->SamplesToExitCountEq <= 0){
+                    pContext->pBundledContext->NumberEffectsEnabled++;
+                }
                 pContext->pBundledContext->SamplesToExitCountEq =
                      (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
                 pContext->pBundledContext->bEqualizerEnabled = LVM_TRUE;
                 break;
             case LVM_VIRTUALIZER:
                 if (pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VIRTUALIZER is already enabled");
+                    LOGV("\tEffect_setEnabled() LVM_VIRTUALIZER is already enabled");
                     return -EINVAL;
                 }
+                if(pContext->pBundledContext->SamplesToExitCountVirt <= 0){
+                    pContext->pBundledContext->NumberEffectsEnabled++;
+                }
                 pContext->pBundledContext->SamplesToExitCountVirt =
                      (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
                 pContext->pBundledContext->bVirtualizerEnabled = LVM_TRUE;
                 break;
             case LVM_VOLUME:
                 if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VOLUME is already enabled");
+                    LOGV("\tEffect_setEnabled() LVM_VOLUME is already enabled");
                     return -EINVAL;
                 }
+                pContext->pBundledContext->NumberEffectsEnabled++;
                 pContext->pBundledContext->bVolumeEnabled = LVM_TRUE;
                 break;
             default:
-                LOGV("\tLVM_ERROR : Effect_setEnabled() invalid effect type");
+                LOGV("\tEffect_setEnabled() invalid effect type");
                 return -EINVAL;
         }
-        pContext->pBundledContext->NumberEffectsEnabled++;
         LvmEffect_enable(pContext);
     } else {
         switch (pContext->EffectType) {
             case LVM_BASS_BOOST:
                 if (pContext->pBundledContext->bBassEnabled == LVM_FALSE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_BASS_BOOST is already disabled");
+                    LOGV("\tEffect_setEnabled() LVM_BASS_BOOST is already disabled");
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bBassEnabled = LVM_FALSE;
                 break;
             case LVM_EQUALIZER:
                 if (pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_EQUALIZER is already disabled");
+                    LOGV("\tEffect_setEnabled() LVM_EQUALIZER is already disabled");
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bEqualizerEnabled = LVM_FALSE;
                 break;
             case LVM_VIRTUALIZER:
                 if (pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VIRTUALIZER is already disabled");
+                    LOGV("\tEffect_setEnabled() LVM_VIRTUALIZER is already disabled");
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bVirtualizerEnabled = LVM_FALSE;
                 break;
             case LVM_VOLUME:
                 if (pContext->pBundledContext->bVolumeEnabled == LVM_FALSE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VOLUME is already disabled");
+                    LOGV("\tEffect_setEnabled() LVM_VOLUME is already disabled");
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bVolumeEnabled = LVM_FALSE;
                 break;
             default:
-                LOGV("\tLVM_ERROR : Effect_setEnabled() invalid effect type");
+                LOGV("\tEffect_setEnabled() invalid effect type");
                 return -EINVAL;
         }
-        pContext->pBundledContext->NumberEffectsEnabled--;
         LvmEffect_disable(pContext);
     }
 
     return 0;
 }
 
+//----------------------------------------------------------------------------
+// LVC_Convert_VolToDb()
+//----------------------------------------------------------------------------
+// Purpose:
+// Convery volume in Q24 to dB
+//
+// Inputs:
+//  vol:   Q.24 volume dB
+//
+//-----------------------------------------------------------------------
+
+int16_t LVC_Convert_VolToDb(uint32_t vol){
+    int16_t  dB;
+
+    dB = LVC_ToDB_s32Tos16(vol <<7);
+    dB = (dB +8)>>4;
+    dB = (dB <-96) ? -96 : dB ;
+
+    return dB;
+}
+
 } // namespace
 } // namespace
 
@@ -2493,32 +2550,31 @@
                               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",
+//LOGV("\tEffect_process Start : Enabled = %d     Called = %d (%8d %8d %8d)",
+//pContext->pBundledContext->NumberEffectsEnabled,pContext->pBundledContext->NumberEffectsCalled,
 //    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");
         return -EINVAL;
     }
+
+    //if(pContext->EffectType == LVM_BASS_BOOST){
+    //  LOGV("\tEffect_process: Effect type is BASS_BOOST");
+    //}else if(pContext->EffectType == LVM_EQUALIZER){
+    //  LOGV("\tEffect_process: Effect type is LVM_EQUALIZER");
+    //}else if(pContext->EffectType == LVM_VIRTUALIZER){
+    //  LOGV("\tEffect_process: Effect type is LVM_VIRTUALIZER");
+    //}
+
     if (inBuffer == NULL  || inBuffer->raw == NULL  ||
             outBuffer == NULL || outBuffer->raw == NULL ||
             inBuffer->frameCount != outBuffer->frameCount){
@@ -2529,70 +2585,57 @@
         (pContext->EffectType == LVM_BASS_BOOST)){
         //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 to turn off BASS_BOOST, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountBb);
         } else {
             status = -ENODATA;
+            pContext->pBundledContext->NumberEffectsEnabled--;
         }
     }
     if ((pContext->pBundledContext->bVolumeEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_VOLUME)){
         //LOGV("\tEffect_process() LVM_VOLUME Effect is not enabled");
         status = -ENODATA;
+        pContext->pBundledContext->NumberEffectsEnabled--;
     }
     if ((pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_EQUALIZER)){
         //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",
+            //LOGV("\tEffect_process: Waiting to turn off EQUALIZER, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountEq);
         } else {
             status = -ENODATA;
+            pContext->pBundledContext->NumberEffectsEnabled--;
         }
     }
     if ((pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_VIRTUALIZER)){
         //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",
+            //LOGV("\tEffect_process: Waiting for to turn off VIRTUALIZER, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountVirt);
         } else {
             status = -ENODATA;
+            pContext->pBundledContext->NumberEffectsEnabled--;
         }
     }
 
-    // If this is the last frame of an effect process its output with no effect
-    if(status == -ENODATA){
-        if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
-            //LOGV("\tLVM_ERROR : Effect_process() accumulating last frame into output buffer");
-            //LOGV("\tLVM_ERROR : Effect_process() trying copying last frame into output buffer");
-            //LOGV("\tLVM_ERROR : Enabled = %d     Called = %d",
-            //pContext->pBundledContext->NumberEffectsEnabled,
-            //pContext->pBundledContext->NumberEffectsCalled);
-
-        }else{
-            //LOGV("\tLVM_ERROR : Effect_process() copying last frame into output buffer");
-        }
-    }
-
-    if((status2Sec != -ENODATA)&&(status != -ENODATA)){
+    if(status != -ENODATA){
         pContext->pBundledContext->NumberEffectsCalled++;
     }
 
     if(pContext->pBundledContext->NumberEffectsCalled ==
        pContext->pBundledContext->NumberEffectsEnabled){
-        //LOGV("\tEffect_process Calling process with %d effects enabled, %d called: Effect %d",
+        //LOGV("\tEffect_process     Calling process with %d effects enabled, %d called: Effect %d",
         //pContext->pBundledContext->NumberEffectsEnabled,
         //pContext->pBundledContext->NumberEffectsCalled, pContext->EffectType);
 
         if(status == -ENODATA){
-            //LOGV("\tLVM_ERROR : Effect_process() actually processing last frame");
+            LOGV("\tEffect_process() processing last frame");
         }
         pContext->pBundledContext->NumberEffectsCalled = 0;
         /* Process all the available frames, block processing is
@@ -2836,10 +2879,10 @@
         case EFFECT_CMD_SET_PARAM:{
             //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_SET_PARAM start");
             if(pContext->EffectType == LVM_BASS_BOOST){
-                //LOGV("\tBassBoost_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)));
+                //LOGV("\tBassBoost_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) +sizeof(int16_t))||
@@ -3038,30 +3081,71 @@
         }
         case EFFECT_CMD_SET_VOLUME:
         {
-            int32_t vol     = *(int32_t *)pCmdData;
-            int16_t dB;
-            int32_t vol_ret[2] = {1<<24,1<<24}; // Apply no volume
+            uint32_t leftVolume, rightVolume;
+            int16_t  leftdB, rightdB;
+            int16_t  maxdB, pandB;
+            int32_t  vol_ret[2] = {1<<24,1<<24}; // Apply no volume
+            int      status = 0;
+            LVM_ControlParams_t     ActiveParams;           /* Current control Parameters */
+            LVM_ReturnStatus_en     LvmStatus=LVM_SUCCESS;  /* Function call status */
 
             // if pReplyData is NULL, VOL_CTRL is delegated to another effect
             if(pReplyData == LVM_NULL){
                 break;
             }
 
-            if(vol==0x1000000){
-                vol -= 1;
+            if (pCmdData == NULL ||
+                cmdSize != 2 * sizeof(uint32_t)) {
+                LOGV("\tLVM_ERROR : Effect_command cmdCode Case: "
+                        "EFFECT_CMD_SET_VOLUME: ERROR");
+                return -EINVAL;
             }
-            // Convert volume linear (Q8.24) to volume dB (0->-96)
-            dB = android::LVC_ToDB_s32Tos16(vol <<7);
-            dB = (dB +8)>>4;
-            dB = (dB <-96) ? -96 : dB ;
 
-            LOGV("\tEFFECT_CMD_SET_VOLUME Session: %d, SessionID: %d VOLUME is %d dB (%d), "
-                  "effect is %d",
-            pContext->pBundledContext->SessionNo, pContext->pBundledContext->SessionId,
-            (int32_t)dB, vol<<7, pContext->EffectType);
+            leftVolume  = ((*(uint32_t *)pCmdData));
+            rightVolume = ((*((uint32_t *)pCmdData + 1)));
+
+            if(leftVolume == 0x1000000){
+                leftVolume -= 1;
+            }
+            if(rightVolume == 0x1000000){
+                rightVolume -= 1;
+            }
+
+            // Convert volume to dB
+            leftdB  = android::LVC_Convert_VolToDb(leftVolume);
+            rightdB = android::LVC_Convert_VolToDb(rightVolume);
+
+            pandB = rightdB - leftdB;
+
+            // Calculate max volume in dB
+            maxdB = leftdB;
+            if(rightdB > maxdB){
+                maxdB = rightdB;
+            }
+            //LOGV("\tEFFECT_CMD_SET_VOLUME Session: %d, SessionID: %d VOLUME is %d dB (%d), "
+            //      "effect is %d",
+            //pContext->pBundledContext->SessionNo, pContext->pBundledContext->SessionId,
+            //(int32_t)maxdB, maxVol<<7, pContext->EffectType);
+            //LOGV("\tEFFECT_CMD_SET_VOLUME: Left is %d, Right is %d", leftVolume, rightVolume);
+            //LOGV("\tEFFECT_CMD_SET_VOLUME: Left %ddB, Right %ddB, Position %ddB",
+            //        leftdB, rightdB, pandB);
 
             memcpy(pReplyData, vol_ret, sizeof(int32_t)*2);
-            android::VolumeSetVolumeLevel(pContext, (int16_t)(dB*100));
+            android::VolumeSetVolumeLevel(pContext, (int16_t)(maxdB*100));
+
+            /* Get the current settings */
+            LvmStatus =LVM_GetControlParameters(pContext->pBundledContext->hInstance,&ActiveParams);
+            LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "VolumeSetStereoPosition")
+            if(LvmStatus != LVM_SUCCESS) return -EINVAL;
+
+            /* Volume parameters */
+            ActiveParams.VC_Balance  = pandB;
+            LOGV("\t\tVolumeSetStereoPosition() (-96dB -> +96dB)-> %d\n", ActiveParams.VC_Balance );
+
+            /* Activate the initial settings */
+            LvmStatus =LVM_SetControlParameters(pContext->pBundledContext->hInstance,&ActiveParams);
+            LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "VolumeSetStereoPosition")
+            if(LvmStatus != LVM_SUCCESS) return -EINVAL;
             break;
          }
         case EFFECT_CMD_SET_AUDIO_MODE:
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index 91963af..2b51029 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -88,12 +88,13 @@
     int                             positionSaved;
     bool                            bMuteEnabled;   /* Must store as mute = -96dB level */
     bool                            bStereoPositionEnabled;
-    int                             frameCount;
     LVM_Fs_en                       SampleRate;
     int                             SamplesPerSecond;
     int                             SamplesToExitCountEq;
     int                             SamplesToExitCountBb;
     int                             SamplesToExitCountVirt;
+    LVM_INT16                       *workBuffer;
+    int                             frameCount;
     #ifdef LVM_PCM
     FILE                            *PcmInPtr;
     FILE                            *PcmOutPtr;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index ffe1983..877e787 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -534,8 +534,9 @@
         if (f) {
             while (!feof(f)) {
                 fgets(buffer, SIZE, f);
-                if (strstr(buffer, " /sdcard/") ||
+                if (strstr(buffer, " /mnt/sdcard/") ||
                     strstr(buffer, " /system/sounds/") ||
+                    strstr(buffer, " /data/") ||
                     strstr(buffer, " /system/media/")) {
                     result.append("  ");
                     result.append(buffer);
@@ -569,8 +570,9 @@
                                 } else {
                                     linkto[len] = 0;
                                 }
-                                if (strstr(linkto, "/sdcard/") == linkto ||
+                                if (strstr(linkto, "/mnt/sdcard/") == linkto ||
                                     strstr(linkto, "/system/sounds/") == linkto ||
+                                    strstr(linkto, "/data/") == linkto ||
                                     strstr(linkto, "/system/media/") == linkto) {
                                     result.append("  ");
                                     result.append(buffer);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 3bf8ae7..472bfcf 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -18,6 +18,7 @@
         HTTPStream.cpp                    \
         JPEGSource.cpp                    \
         MP3Extractor.cpp                  \
+        MPEG2TSWriter.cpp                 \
         MPEG4Extractor.cpp                \
         MPEG4Writer.cpp                   \
         MediaBuffer.cpp                   \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index c27cfc8..b314114 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -27,9 +27,13 @@
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 
+#include "include/AwesomePlayer.h"
+
 namespace android {
 
-AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink)
+AudioPlayer::AudioPlayer(
+        const sp<MediaPlayerBase::AudioSink> &audioSink,
+        AwesomePlayer *observer)
     : mAudioTrack(NULL),
       mInputBuffer(NULL),
       mSampleRate(0),
@@ -45,7 +49,8 @@
       mIsFirstBuffer(false),
       mFirstBufferResult(OK),
       mFirstBuffer(NULL),
-      mAudioSink(audioSink) {
+      mAudioSink(audioSink),
+      mObserver(observer) {
 }
 
 AudioPlayer::~AudioPlayer() {
@@ -301,6 +306,9 @@
                 }
 
                 mSeeking = false;
+                if (mObserver) {
+                    mObserver->postAudioSeekComplete();
+                }
             }
         }
 
@@ -323,6 +331,10 @@
             Mutex::Autolock autoLock(mLock);
 
             if (err != OK) {
+                if (mObserver && !mReachedEOS) {
+                    mObserver->postAudioEOS();
+                }
+
                 mReachedEOS = true;
                 mFinalStatus = err;
                 break;
@@ -411,6 +423,12 @@
     mReachedEOS = false;
     mSeekTimeUs = time_us;
 
+    if (mAudioSink != NULL) {
+        mAudioSink->flush();
+    } else {
+        mAudioTrack->flush();
+    }
+
     return OK;
 }
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index fd5f30b..97c9003 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -642,7 +642,7 @@
     if (mAudioSource != NULL) {
         if (mAudioPlayer == NULL) {
             if (mAudioSink != NULL) {
-                mAudioPlayer = new AudioPlayer(mAudioSink);
+                mAudioPlayer = new AudioPlayer(mAudioSink, this);
                 mAudioPlayer->setSource(mAudioSource);
 
                 // We've already started the MediaSource in order to enable
@@ -669,8 +669,6 @@
         } else {
             mAudioPlayer->resume();
         }
-
-        postCheckAudioStatusEvent_l();
     }
 
     if (mTimeSource == NULL && mAudioPlayer == NULL) {
@@ -1191,7 +1189,7 @@
         return;
     }
     mAudioStatusEventPending = true;
-    mQueue.postEventWithDelay(mCheckAudioStatusEvent, 100000ll);
+    mQueue.postEvent(mCheckAudioStatusEvent);
 }
 
 void AwesomePlayer::onCheckAudioStatus() {
@@ -1222,8 +1220,6 @@
         mFlags |= FIRST_FRAME;
         postStreamDoneEvent_l(finalStatus);
     }
-
-    postCheckAudioStatusEvent_l();
 }
 
 status_t AwesomePlayer::prepare() {
@@ -1685,5 +1681,13 @@
     return mExtractorFlags;
 }
 
+void AwesomePlayer::postAudioEOS() {
+    postCheckAudioStatusEvent_l();
+}
+
+void AwesomePlayer::postAudioSeekComplete() {
+    postCheckAudioStatusEvent_l();
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
new file mode 100644
index 0000000..ee74b88
--- /dev/null
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -0,0 +1,758 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "MPEG2TSWriter"
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MPEG2TSWriter.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include "include/ESDS.h"
+
+namespace android {
+
+struct MPEG2TSWriter::SourceInfo : public AHandler {
+    SourceInfo(const sp<MediaSource> &source);
+
+    void start(const sp<AMessage> &notify);
+    void stop();
+
+    unsigned streamType() const;
+    unsigned incrementContinuityCounter();
+
+    enum {
+        kNotifyStartFailed,
+        kNotifyBuffer,
+        kNotifyReachedEOS,
+    };
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+    virtual ~SourceInfo();
+
+private:
+    enum {
+        kWhatStart = 'strt',
+        kWhatRead  = 'read',
+    };
+
+    sp<MediaSource> mSource;
+    sp<ALooper> mLooper;
+    sp<AMessage> mNotify;
+
+    sp<ABuffer> mAACBuffer;
+
+    unsigned mStreamType;
+    unsigned mContinuityCounter;
+
+    void extractCodecSpecificData();
+
+    void appendAACFrames(MediaBuffer *buffer);
+    void flushAACFrames();
+
+    void postAVCFrame(MediaBuffer *buffer);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SourceInfo);
+};
+
+MPEG2TSWriter::SourceInfo::SourceInfo(const sp<MediaSource> &source)
+    : mSource(source),
+      mLooper(new ALooper),
+      mStreamType(0),
+      mContinuityCounter(0) {
+    mLooper->setName("MPEG2TSWriter source");
+
+    sp<MetaData> meta = mSource->getFormat();
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        mStreamType = 0x0f;
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        mStreamType = 0x1b;
+    } else {
+        TRESPASS();
+    }
+}
+
+MPEG2TSWriter::SourceInfo::~SourceInfo() {
+}
+
+unsigned MPEG2TSWriter::SourceInfo::streamType() const {
+    return mStreamType;
+}
+
+unsigned MPEG2TSWriter::SourceInfo::incrementContinuityCounter() {
+    if (++mContinuityCounter == 16) {
+        mContinuityCounter = 0;
+    }
+
+    return mContinuityCounter;
+}
+
+void MPEG2TSWriter::SourceInfo::start(const sp<AMessage> &notify) {
+    mLooper->registerHandler(this);
+    mLooper->start();
+
+    mNotify = notify;
+
+    (new AMessage(kWhatStart, id()))->post();
+}
+
+void MPEG2TSWriter::SourceInfo::stop() {
+    mLooper->unregisterHandler(id());
+    mLooper->stop();
+}
+
+void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() {
+    sp<MetaData> meta = mSource->getFormat();
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        return;
+    }
+
+    sp<ABuffer> out = new ABuffer(1024);
+    out->setRange(0, 0);
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    CHECK(meta->findData(kKeyAVCC, &type, &data, &size));
+
+    const uint8_t *ptr = (const uint8_t *)data;
+
+    size_t numSeqParameterSets = ptr[5] & 31;
+
+    ptr += 6;
+    size -= 6;
+
+    for (size_t i = 0; i < numSeqParameterSets; ++i) {
+        CHECK(size >= 2);
+        size_t length = U16_AT(ptr);
+
+        ptr += 2;
+        size -= 2;
+
+        CHECK(size >= length);
+
+        CHECK_LE(out->size() + 4 + length, out->capacity());
+        memcpy(out->data() + out->size(), "\x00\x00\x00\x01", 4);
+        memcpy(out->data() + out->size() + 4, ptr, length);
+        out->setRange(0, out->size() + length + 4);
+
+        ptr += length;
+        size -= length;
+    }
+
+    CHECK(size >= 1);
+    size_t numPictureParameterSets = *ptr;
+    ++ptr;
+    --size;
+
+    for (size_t i = 0; i < numPictureParameterSets; ++i) {
+        CHECK(size >= 2);
+        size_t length = U16_AT(ptr);
+
+        ptr += 2;
+        size -= 2;
+
+        CHECK(size >= length);
+
+        CHECK_LE(out->size() + 4 + length, out->capacity());
+        memcpy(out->data() + out->size(), "\x00\x00\x00\x01", 4);
+        memcpy(out->data() + out->size() + 4, ptr, length);
+        out->setRange(0, out->size() + length + 4);
+
+        ptr += length;
+        size -= length;
+    }
+
+    out->meta()->setInt64("timeUs", 0ll);
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kNotifyBuffer);
+    notify->setObject("buffer", out);
+    notify->post();
+}
+
+void MPEG2TSWriter::SourceInfo::postAVCFrame(MediaBuffer *buffer) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kNotifyBuffer);
+
+    sp<ABuffer> copy =
+        new ABuffer(buffer->range_length());
+    memcpy(copy->data(),
+           (const uint8_t *)buffer->data()
+            + buffer->range_offset(),
+           buffer->range_length());
+
+    int64_t timeUs;
+    CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+    copy->meta()->setInt64("timeUs", timeUs);
+
+    int32_t isSync;
+    if (buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync)
+            && isSync != 0) {
+        copy->meta()->setInt32("isSync", true);
+    }
+
+    notify->setObject("buffer", copy);
+    notify->post();
+}
+
+void MPEG2TSWriter::SourceInfo::appendAACFrames(MediaBuffer *buffer) {
+    if (mAACBuffer != NULL
+            && mAACBuffer->size() + 7 + buffer->range_length()
+                    > mAACBuffer->capacity()) {
+        flushAACFrames();
+    }
+
+    if (mAACBuffer == NULL) {
+        size_t alloc = 4096;
+        if (buffer->range_length() + 7 > alloc) {
+            alloc = 7 + buffer->range_length();
+        }
+
+        mAACBuffer = new ABuffer(alloc);
+
+        int64_t timeUs;
+        CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+        mAACBuffer->meta()->setInt64("timeUs", timeUs);
+        mAACBuffer->meta()->setInt32("isSync", true);
+
+        mAACBuffer->setRange(0, 0);
+    }
+
+    sp<MetaData> meta = mSource->getFormat();
+    uint32_t type;
+    const void *data;
+    size_t size;
+    CHECK(meta->findData(kKeyESDS, &type, &data, &size));
+
+    ESDS esds((const char *)data, size);
+    CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+    const uint8_t *codec_specific_data;
+    size_t codec_specific_data_size;
+    esds.getCodecSpecificInfo(
+            (const void **)&codec_specific_data, &codec_specific_data_size);
+
+    CHECK_GE(codec_specific_data_size, 2u);
+
+    unsigned profile = (codec_specific_data[0] >> 3) - 1;
+
+    unsigned sampling_freq_index =
+        ((codec_specific_data[0] & 7) << 1)
+        | (codec_specific_data[1] >> 7);
+
+    unsigned channel_configuration =
+        (codec_specific_data[1] >> 3) & 0x0f;
+
+    uint8_t *ptr = mAACBuffer->data() + mAACBuffer->size();
+
+    const uint32_t aac_frame_length = buffer->range_length() + 7;
+
+    *ptr++ = 0xff;
+    *ptr++ = 0xf1;  // b11110001, ID=0, layer=0, protection_absent=1
+
+    *ptr++ =
+        profile << 6
+        | sampling_freq_index << 2
+        | ((channel_configuration >> 2) & 1);  // private_bit=0
+
+    // original_copy=0, home=0, copyright_id_bit=0, copyright_id_start=0
+    *ptr++ =
+        (channel_configuration & 3) << 6
+        | aac_frame_length >> 11;
+    *ptr++ = (aac_frame_length >> 3) & 0xff;
+    *ptr++ = (aac_frame_length & 7) << 5;
+
+    // adts_buffer_fullness=0, number_of_raw_data_blocks_in_frame=0
+    *ptr++ = 0;
+
+    memcpy(ptr,
+           (const uint8_t *)buffer->data() + buffer->range_offset(),
+           buffer->range_length());
+
+    ptr += buffer->range_length();
+
+    mAACBuffer->setRange(0, ptr - mAACBuffer->data());
+}
+
+void MPEG2TSWriter::SourceInfo::flushAACFrames() {
+    if (mAACBuffer == NULL) {
+        return;
+    }
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kNotifyBuffer);
+    notify->setObject("buffer", mAACBuffer);
+    notify->post();
+
+    mAACBuffer.clear();
+}
+
+void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatStart:
+        {
+            status_t err = mSource->start();
+            if (err != OK) {
+                sp<AMessage> notify = mNotify->dup();
+                notify->setInt32("what", kNotifyStartFailed);
+                notify->post();
+                break;
+            }
+
+            extractCodecSpecificData();
+
+            (new AMessage(kWhatRead, id()))->post();
+            break;
+        }
+
+        case kWhatRead:
+        {
+            MediaBuffer *buffer;
+            status_t err = mSource->read(&buffer);
+
+            if (err != OK && err != INFO_FORMAT_CHANGED) {
+                if (mStreamType == 0x0f) {
+                    flushAACFrames();
+                }
+
+                sp<AMessage> notify = mNotify->dup();
+                notify->setInt32("what", kNotifyReachedEOS);
+                notify->setInt32("status", err);
+                notify->post();
+                break;
+            }
+
+            if (err == OK) {
+                if (buffer->range_length() > 0) {
+                    if (mStreamType == 0x0f) {
+                        appendAACFrames(buffer);
+                    } else {
+                        postAVCFrame(buffer);
+                    }
+                }
+
+                buffer->release();
+                buffer = NULL;
+            }
+
+            msg->post();
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG2TSWriter::MPEG2TSWriter(const char *filename)
+    : mFile(fopen(filename, "wb")),
+      mStarted(false),
+      mNumSourcesDone(0),
+      mNumTSPacketsWritten(0),
+      mNumTSPacketsBeforeMeta(0) {
+    CHECK(mFile != NULL);
+
+    mLooper = new ALooper;
+    mLooper->setName("MPEG2TSWriter");
+
+    mReflector = new AHandlerReflector<MPEG2TSWriter>(this);
+
+    mLooper->registerHandler(mReflector);
+    mLooper->start();
+}
+
+MPEG2TSWriter::~MPEG2TSWriter() {
+    mLooper->unregisterHandler(mReflector->id());
+    mLooper->stop();
+
+    fclose(mFile);
+    mFile = NULL;
+}
+
+status_t MPEG2TSWriter::addSource(const sp<MediaSource> &source) {
+    CHECK(!mStarted);
+
+    sp<MetaData> meta = source->getFormat();
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
+            && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    sp<SourceInfo> info = new SourceInfo(source);
+
+    mSources.push(info);
+
+    return OK;
+}
+
+status_t MPEG2TSWriter::start(MetaData *param) {
+    CHECK(!mStarted);
+
+    mStarted = true;
+    mNumSourcesDone = 0;
+    mNumTSPacketsWritten = 0;
+    mNumTSPacketsBeforeMeta = 0;
+
+    for (size_t i = 0; i < mSources.size(); ++i) {
+        sp<AMessage> notify =
+            new AMessage(kWhatSourceNotify, mReflector->id());
+
+        notify->setInt32("source-index", i);
+
+        mSources.editItemAt(i)->start(notify);
+    }
+
+    return OK;
+}
+
+status_t MPEG2TSWriter::stop() {
+    CHECK(mStarted);
+
+    for (size_t i = 0; i < mSources.size(); ++i) {
+        mSources.editItemAt(i)->stop();
+    }
+    mStarted = false;
+
+    return OK;
+}
+
+status_t MPEG2TSWriter::pause() {
+    CHECK(mStarted);
+
+    return OK;
+}
+
+bool MPEG2TSWriter::reachedEOS() {
+    return !mStarted || (mNumSourcesDone == mSources.size() ? true : false);
+}
+
+status_t MPEG2TSWriter::dump(int fd, const Vector<String16> &args) {
+    return OK;
+}
+
+void MPEG2TSWriter::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSourceNotify:
+        {
+            int32_t sourceIndex;
+            CHECK(msg->findInt32("source-index", &sourceIndex));
+
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            if (what == SourceInfo::kNotifyReachedEOS
+                    || what == SourceInfo::kNotifyStartFailed) {
+                ++mNumSourcesDone;
+            } else if (what == SourceInfo::kNotifyBuffer) {
+                sp<RefBase> obj;
+                CHECK(msg->findObject("buffer", &obj));
+
+                writeTS();
+
+                sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+                writeAccessUnit(sourceIndex, buffer);
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MPEG2TSWriter::writeProgramAssociationTable() {
+    // 0x47
+    // transport_error_indicator = b0
+    // payload_unit_start_indicator = b1
+    // transport_priority = b0
+    // PID = b0000000000000 (13 bits)
+    // transport_scrambling_control = b00
+    // adaptation_field_control = b01 (no adaptation field, payload only)
+    // continuity_counter = b????
+    // skip = 0x00
+    // --- payload follows
+    // table_id = 0x00
+    // section_syntax_indicator = b1
+    // must_be_zero = b0
+    // reserved = b11
+    // section_length = 0x00d
+    // transport_stream_id = 0x0000
+    // reserved = b11
+    // version_number = b00001
+    // current_next_indicator = b1
+    // section_number = 0x00
+    // last_section_number = 0x00
+    //   one program follows:
+    //   program_number = 0x0001
+    //   reserved = b111
+    //   program_map_PID = 0x01e0 (13 bits!)
+    // CRC = 0x????????
+
+    static const uint8_t kData[] = {
+        0x47,
+        0x40, 0x00, 0x10, 0x00,  // b0100 0000 0000 0000 0001 ???? 0000 0000
+        0x00, 0xb0, 0x0d, 0x00,  // b0000 0000 1011 0000 0000 1101 0000 0000
+        0x00, 0xc3, 0x00, 0x00,  // b0000 0000 1100 0011 0000 0000 0000 0000
+        0x00, 0x01, 0xe1, 0xe0,  // b0000 0000 0000 0001 1110 0001 1110 0000
+        0x00, 0x00, 0x00, 0x00   // b???? ???? ???? ???? ???? ???? ???? ????
+    };
+
+    sp<ABuffer> buffer = new ABuffer(188);
+    memset(buffer->data(), 0, buffer->size());
+    memcpy(buffer->data(), kData, sizeof(kData));
+
+    static const unsigned kContinuityCounter = 5;
+    buffer->data()[3] |= kContinuityCounter;
+
+    CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size());
+}
+
+void MPEG2TSWriter::writeProgramMap() {
+    // 0x47
+    // transport_error_indicator = b0
+    // payload_unit_start_indicator = b1
+    // transport_priority = b0
+    // PID = b0 0001 1110 0000 (13 bits) [0x1e0]
+    // transport_scrambling_control = b00
+    // adaptation_field_control = b01 (no adaptation field, payload only)
+    // continuity_counter = b????
+    // skip = 0x00
+    // -- payload follows
+    // table_id = 0x02
+    // section_syntax_indicator = b1
+    // must_be_zero = b0
+    // reserved = b11
+    // section_length = 0x???
+    // program_number = 0x0001
+    // reserved = b11
+    // version_number = b00001
+    // current_next_indicator = b1
+    // section_number = 0x00
+    // last_section_number = 0x00
+    // reserved = b111
+    // PCR_PID = b? ???? ???? ???? (13 bits)
+    // reserved = b1111
+    // program_info_length = 0x000
+    //   one or more elementary stream descriptions follow:
+    //   stream_type = 0x??
+    //   reserved = b111
+    //   elementary_PID = b? ???? ???? ???? (13 bits)
+    //   reserved = b1111
+    //   ES_info_length = 0x000
+    // CRC = 0x????????
+
+    static const uint8_t kData[] = {
+        0x47,
+        0x41, 0xe0, 0x10, 0x00,  // b0100 0001 1110 0000 0001 ???? 0000 0000
+        0x02, 0xb0, 0x00, 0x00,  // b0000 0010 1011 ???? ???? ???? 0000 0000
+        0x01, 0xc3, 0x00, 0x00,  // b0000 0001 1100 0011 0000 0000 0000 0000
+        0xe0, 0x00, 0xf0, 0x00   // b111? ???? ???? ???? 1111 0000 0000 0000
+    };
+
+    sp<ABuffer> buffer = new ABuffer(188);
+    memset(buffer->data(), 0, buffer->size());
+    memcpy(buffer->data(), kData, sizeof(kData));
+
+    static const unsigned kContinuityCounter = 5;
+    buffer->data()[3] |= kContinuityCounter;
+
+    size_t section_length = 5 * mSources.size() + 4 + 9;
+    buffer->data()[6] |= section_length >> 8;
+    buffer->data()[7] = section_length & 0xff;
+
+    static const unsigned kPCR_PID = 0x1e1;
+    buffer->data()[13] |= (kPCR_PID >> 8) & 0x1f;
+    buffer->data()[14] = kPCR_PID & 0xff;
+
+    uint8_t *ptr = &buffer->data()[sizeof(kData)];
+    for (size_t i = 0; i < mSources.size(); ++i) {
+        *ptr++ = mSources.editItemAt(i)->streamType();
+
+        const unsigned ES_PID = 0x1e0 + i + 1;
+        *ptr++ = 0xe0 | (ES_PID >> 8);
+        *ptr++ = ES_PID & 0xff;
+        *ptr++ = 0xf0;
+        *ptr++ = 0x00;
+    }
+
+    *ptr++ = 0x00;
+    *ptr++ = 0x00;
+    *ptr++ = 0x00;
+    *ptr++ = 0x00;
+
+    CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size());
+}
+
+void MPEG2TSWriter::writeAccessUnit(
+        int32_t sourceIndex, const sp<ABuffer> &accessUnit) {
+    // 0x47
+    // transport_error_indicator = b0
+    // payload_unit_start_indicator = b1
+    // transport_priority = b0
+    // PID = b0 0001 1110 ???? (13 bits) [0x1e0 + 1 + sourceIndex]
+    // transport_scrambling_control = b00
+    // adaptation_field_control = b01 (no adaptation field, payload only)
+    // continuity_counter = b????
+    // -- payload follows
+    // packet_startcode_prefix = 0x000001
+    // stream_id = 0x?? (0xe0 for avc video, 0xc0 for aac audio)
+    // PES_packet_length = 0x????
+    // reserved = b10
+    // PES_scrambling_control = b00
+    // PES_priority = b0
+    // data_alignment_indicator = b1
+    // copyright = b0
+    // original_or_copy = b0
+    // PTS_DTS_flags = b10  (PTS only)
+    // ESCR_flag = b0
+    // ES_rate_flag = b0
+    // DSM_trick_mode_flag = b0
+    // additional_copy_info_flag = b0
+    // PES_CRC_flag = b0
+    // PES_extension_flag = b0
+    // PES_header_data_length = 0x05
+    // reserved = b0010 (PTS)
+    // PTS[32..30] = b???
+    // reserved = b1
+    // PTS[29..15] = b??? ???? ???? ???? (15 bits)
+    // reserved = b1
+    // PTS[14..0] = b??? ???? ???? ???? (15 bits)
+    // reserved = b1
+    // the first fragment of "buffer" follows
+
+    sp<ABuffer> buffer = new ABuffer(188);
+    memset(buffer->data(), 0, buffer->size());
+
+    const unsigned PID = 0x1e0 + sourceIndex + 1;
+
+    const unsigned continuity_counter =
+        mSources.editItemAt(sourceIndex)->incrementContinuityCounter();
+
+    // XXX if there are multiple streams of a kind (more than 1 audio or
+    // more than 1 video) they need distinct stream_ids.
+    const unsigned stream_id =
+        mSources.editItemAt(sourceIndex)->streamType() == 0x0f ? 0xc0 : 0xe0;
+
+    int64_t timeUs;
+    CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+    uint32_t PTS = (timeUs * 9ll) / 100ll;
+
+    size_t PES_packet_length = accessUnit->size() + 8;
+
+    uint8_t *ptr = buffer->data();
+    *ptr++ = 0x47;
+    *ptr++ = 0x40 | (PID >> 8);
+    *ptr++ = PID & 0xff;
+    *ptr++ = 0x10 | continuity_counter;
+    *ptr++ = 0x00;
+    *ptr++ = 0x00;
+    *ptr++ = 0x01;
+    *ptr++ = stream_id;
+    *ptr++ = PES_packet_length >> 8;
+    *ptr++ = PES_packet_length & 0xff;
+    *ptr++ = 0x84;
+    *ptr++ = 0x80;
+    *ptr++ = 0x05;
+    *ptr++ = 0x20 | (((PTS >> 30) & 7) << 1) | 1;
+    *ptr++ = (PTS >> 22) & 0xff;
+    *ptr++ = (((PTS >> 15) & 0x7f) << 1) | 1;
+    *ptr++ = (PTS >> 7) & 0xff;
+    *ptr++ = ((PTS & 0x7f) << 1) | 1;
+
+    size_t sizeLeft = buffer->data() + buffer->size() - ptr;
+    size_t copy = accessUnit->size();
+    if (copy > sizeLeft) {
+        copy = sizeLeft;
+    }
+
+    memcpy(ptr, accessUnit->data(), copy);
+
+    CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size());
+
+    size_t offset = copy;
+    while (offset < accessUnit->size()) {
+        // for subsequent fragments of "buffer":
+        // 0x47
+        // transport_error_indicator = b0
+        // payload_unit_start_indicator = b0
+        // transport_priority = b0
+        // PID = b0 0001 1110 ???? (13 bits) [0x1e0 + 1 + sourceIndex]
+        // transport_scrambling_control = b00
+        // adaptation_field_control = b01 (no adaptation field, payload only)
+        // continuity_counter = b????
+        // the fragment of "buffer" follows.
+
+        memset(buffer->data(), 0, buffer->size());
+
+        const unsigned continuity_counter =
+            mSources.editItemAt(sourceIndex)->incrementContinuityCounter();
+
+        ptr = buffer->data();
+        *ptr++ = 0x47;
+        *ptr++ = 0x00 | (PID >> 8);
+        *ptr++ = PID & 0xff;
+        *ptr++ = 0x10 | continuity_counter;
+
+        size_t sizeLeft = buffer->data() + buffer->size() - ptr;
+        size_t copy = accessUnit->size() - offset;
+        if (copy > sizeLeft) {
+            copy = sizeLeft;
+        }
+
+        memcpy(ptr, accessUnit->data() + offset, copy);
+        CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile),
+                 buffer->size());
+
+        offset += copy;
+    }
+}
+
+void MPEG2TSWriter::writeTS() {
+    if (mNumTSPacketsWritten >= mNumTSPacketsBeforeMeta) {
+        writeProgramAssociationTable();
+        writeProgramMap();
+
+        mNumTSPacketsBeforeMeta = mNumTSPacketsWritten + 2500;
+    }
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 7a8cf32..43938b2 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -93,7 +93,10 @@
     sp<DataSource> mSource;
     off_t mOffset;
     Page mCurrentPage;
+    uint64_t mPrevGranulePosition;
     size_t mCurrentPageSize;
+    bool mFirstPacketInPage;
+    uint64_t mCurrentPageSamples;
     size_t mNextLaceIndex;
 
     off_t mFirstDataOffset;
@@ -113,6 +116,8 @@
     void parseFileMetaData();
     void extractAlbumArt(const void *data, size_t size);
 
+    uint64_t findPrevGranulePosition(off_t pageOffset);
+
     MyVorbisExtractor(const MyVorbisExtractor &);
     MyVorbisExtractor &operator=(const MyVorbisExtractor &);
 };
@@ -193,7 +198,10 @@
 MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source)
     : mSource(source),
       mOffset(0),
+      mPrevGranulePosition(0),
       mCurrentPageSize(0),
+      mFirstPacketInPage(true),
+      mCurrentPageSamples(0),
       mNextLaceIndex(0),
       mFirstDataOffset(-1) {
     mCurrentPage.mNumSegments = 0;
@@ -238,6 +246,52 @@
     }
 }
 
+// Given the offset of the "current" page, find the page immediately preceding
+// it (if any) and return its granule position.
+// To do this we back up from the "current" page's offset until we find any
+// page preceding it and then scan forward to just before the current page.
+uint64_t MyVorbisExtractor::findPrevGranulePosition(off_t pageOffset) {
+    off_t prevPageOffset = 0;
+    off_t prevGuess = pageOffset;
+    for (;;) {
+        if (prevGuess >= 5000) {
+            prevGuess -= 5000;
+        } else {
+            prevGuess = 0;
+        }
+
+        LOGV("backing up %ld bytes", pageOffset - prevGuess);
+
+        CHECK_EQ(findNextPage(prevGuess, &prevPageOffset), (status_t)OK);
+
+        if (prevPageOffset < pageOffset || prevGuess == 0) {
+            break;
+        }
+    }
+
+    if (prevPageOffset == pageOffset) {
+        // We did not find a page preceding this one.
+        return 0;
+    }
+
+    LOGV("prevPageOffset at %ld, pageOffset at %ld", prevPageOffset, pageOffset);
+
+    for (;;) {
+        Page prevPage;
+        ssize_t n = readPage(prevPageOffset, &prevPage);
+
+        if (n <= 0) {
+            return 0;
+        }
+
+        prevPageOffset += n;
+
+        if (prevPageOffset == pageOffset) {
+            return prevPage.mGranulePosition;
+        }
+    }
+}
+
 status_t MyVorbisExtractor::seekToOffset(off_t offset) {
     if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) {
         // Once we know where the actual audio data starts (past the headers)
@@ -252,9 +306,16 @@
         return err;
     }
 
+    // We found the page we wanted to seek to, but we'll also need
+    // the page preceding it to determine how many valid samples are on
+    // this page.
+    mPrevGranulePosition = findPrevGranulePosition(pageOffset);
+
     mOffset = pageOffset;
 
     mCurrentPageSize = 0;
+    mFirstPacketInPage = true;
+    mCurrentPageSamples = 0;
     mCurrentPage.mNumSegments = 0;
     mNextLaceIndex = 0;
 
@@ -399,6 +460,12 @@
                     buffer->meta_data()->setInt64(kKeyTime, timeUs);
                 }
 
+                if (mFirstPacketInPage) {
+                    buffer->meta_data()->setInt32(
+                            kKeyValidSamples, mCurrentPageSamples);
+                    mFirstPacketInPage = false;
+                }
+
                 *out = buffer;
 
                 return OK;
@@ -423,6 +490,12 @@
             return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
         }
 
+        mCurrentPageSamples =
+            mCurrentPage.mGranulePosition - mPrevGranulePosition;
+        mFirstPacketInPage = true;
+
+        mPrevGranulePosition = mCurrentPage.mGranulePosition;
+
         mCurrentPageSize = n;
         mNextLaceIndex = 0;
 
@@ -435,6 +508,10 @@
                     buffer->meta_data()->setInt64(kKeyTime, timeUs);
                 }
 
+                buffer->meta_data()->setInt32(
+                        kKeyValidSamples, mCurrentPageSamples);
+                mFirstPacketInPage = false;
+
                 *out = buffer;
 
                 return OK;
diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
index e4ed5e6..f58c16d 100644
--- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp
+++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
@@ -171,6 +171,10 @@
             mInputBuffer->release();
             mInputBuffer = NULL;
         }
+
+        // Make sure that the next buffer output does not still
+        // depend on fragments from the last one decoded.
+        PVMP4AudioDecoderResetBuffer(mDecoderBuf);
     } else {
         seekTimeUs = -1;
     }
diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
index c4a8280..59dd740 100644
--- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
@@ -132,6 +132,10 @@
             mInputBuffer->release();
             mInputBuffer = NULL;
         }
+
+        // Make sure that the next buffer output does not still
+        // depend on fragments from the last one decoded.
+        pvmp3_InitDecoder(mConfig, mDecoderBuf);
     } else {
         seekTimeUs = -1;
     }
diff --git a/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp
index 53f0638..703b41e 100644
--- a/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp
+++ b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VorbisDecoder"
+#include <utils/Log.h>
+
 #include "VorbisDecoder.h"
 
 #include <media/stagefright/MediaBufferGroup.h>
@@ -108,6 +112,7 @@
 
     mAnchorTimeUs = 0;
     mNumFramesOutput = 0;
+    mNumFramesLeftOnPage = 0;
     mStarted = true;
 
     return OK;
@@ -188,6 +193,13 @@
         }
     }
 
+    if (numFrames > mNumFramesLeftOnPage) {
+        LOGV("discarding %d frames at end of page",
+             numFrames - mNumFramesLeftOnPage);
+        numFrames = mNumFramesLeftOnPage;
+    }
+    mNumFramesLeftOnPage -= numFrames;
+
     out->set_range(0, numFrames * sizeof(int16_t) * mNumChannels);
 
     return numFrames;
@@ -226,6 +238,12 @@
         CHECK(seekTimeUs < 0);
     }
 
+    int32_t numPageSamples;
+    if (inputBuffer->meta_data()->findInt32(
+                kKeyValidSamples, &numPageSamples)) {
+        mNumFramesLeftOnPage = numPageSamples;
+    }
+
     MediaBuffer *outputBuffer;
     CHECK_EQ(mBufferGroup->acquire_buffer(&outputBuffer), OK);
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index ea2f7d5..db98253 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -93,6 +93,9 @@
     // This is a mask of MediaExtractor::Flags.
     uint32_t flags() const;
 
+    void postAudioEOS();
+    void postAudioSeekComplete();
+
 private:
     friend struct AwesomeEvent;
 
diff --git a/media/libstagefright/include/VorbisDecoder.h b/media/libstagefright/include/VorbisDecoder.h
index e9a488a..13e8b77 100644
--- a/media/libstagefright/include/VorbisDecoder.h
+++ b/media/libstagefright/include/VorbisDecoder.h
@@ -55,6 +55,7 @@
     int32_t mSampleRate;
     int64_t mAnchorTimeUs;
     int64_t mNumFramesOutput;
+    int32_t mNumFramesLeftOnPage;
 
     vorbis_dsp_state *mState;
     vorbis_info *mVi;
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 9952783..47cca80 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -389,20 +389,23 @@
 
         // ES data follows.
 
-        onPayloadData(
-                PTS_DTS_flags, PTS, DTS,
-                br->data(), br->numBitsLeft() / 8);
-
         if (PES_packet_length != 0) {
             CHECK_GE(PES_packet_length, PES_header_data_length + 3);
 
             unsigned dataLength =
                 PES_packet_length - 3 - PES_header_data_length;
 
-            CHECK_EQ(br->numBitsLeft(), dataLength * 8);
+            CHECK_GE(br->numBitsLeft(), dataLength * 8);
+
+            onPayloadData(
+                    PTS_DTS_flags, PTS, DTS, br->data(), dataLength);
 
             br->skipBits(dataLength * 8);
         } else {
+            onPayloadData(
+                    PTS_DTS_flags, PTS, DTS,
+                    br->data(), br->numBitsLeft() / 8);
+
             size_t payloadSizeBits = br->numBitsLeft();
             CHECK((payloadSizeBits % 8) == 0);
 
@@ -491,7 +494,7 @@
     CHECK(picParamSet != NULL);
 
     buffer->setRange(stopOffset, size - stopOffset);
-    LOGI("buffer has %d bytes left.", buffer->size());
+    LOGV("buffer has %d bytes left.", buffer->size());
 
     size_t csdSize =
         1 + 3 + 1 + 1
@@ -527,6 +530,8 @@
     const uint8_t *data = *_data;
     size_t size = *_size;
 
+    // hexdump(data, size);
+
     *nalStart = NULL;
     *nalSize = 0;
 
@@ -572,18 +577,23 @@
         ++offset;
     }
 
-    CHECK_LT(offset + 2, size);
-
     *nalStart = &data[startOffset];
     *nalSize = endOffset - startOffset;
 
-    *_data = &data[offset];
-    *_size = size - offset;
+    if (offset + 2 < size) {
+        *_data = &data[offset];
+        *_size = size - offset;
+    } else {
+        *_data = NULL;
+        *_size = 0;
+    }
 
     return true;
 }
 
 sp<ABuffer> MakeCleanAVCData(const uint8_t *data, size_t size) {
+    // hexdump(data, size);
+
     const uint8_t *tmpData = data;
     size_t tmpSize = size;
 
@@ -591,6 +601,7 @@
     const uint8_t *nalStart;
     size_t nalSize;
     while (getNextNALUnit(&tmpData, &tmpSize, &nalStart, &nalSize)) {
+        // hexdump(nalStart, nalSize);
         totalSize += 4 + nalSize;
     }
 
@@ -615,15 +626,15 @@
     CHECK_EQ(br.getBits(2), 0u);
     br.getBits(1);  // protection_absent
     unsigned profile = br.getBits(2);
-    LOGI("profile = %u", profile);
+    LOGV("profile = %u", profile);
     CHECK_NE(profile, 3u);
     unsigned sampling_freq_index = br.getBits(4);
     br.getBits(1);  // private_bit
     unsigned channel_configuration = br.getBits(3);
     CHECK_NE(channel_configuration, 0u);
 
-    LOGI("sampling_freq_index = %u", sampling_freq_index);
-    LOGI("channel_configuration = %u", channel_configuration);
+    LOGV("sampling_freq_index = %u", sampling_freq_index);
+    LOGV("channel_configuration = %u", channel_configuration);
 
     CHECK_LE(sampling_freq_index, 11u);
     static const int32_t kSamplingFreq[] = {
@@ -707,8 +718,8 @@
             sp<ABuffer> csd =
                 FindMPEG2ADTSConfig(buffer, &sampleRate, &channelCount);
 
-            LOGI("sampleRate = %d", sampleRate);
-            LOGI("channelCount = %d", channelCount);
+            LOGV("sampleRate = %d", sampleRate);
+            LOGV("channelCount = %d", channelCount);
 
             meta->setInt32(kKeySampleRate, sampleRate);
             meta->setInt32(kKeyChannelCount, channelCount);
@@ -716,7 +727,7 @@
             meta->setData(kKeyESDS, 0, csd->data(), csd->size());
         }
 
-        LOGI("created source!");
+        LOGV("created source!");
         mSource = new AnotherPacketSource(meta);
 
         // fall through
@@ -915,7 +926,10 @@
     unsigned adaptation_field_control = br->getBits(2);
     LOGV("adaptation_field_control = %u", adaptation_field_control);
 
-    MY_LOGV("continuity_counter = %u", br->getBits(4));
+    unsigned continuity_counter = br->getBits(4);
+    LOGV("continuity_counter = %u", continuity_counter);
+
+    // LOGI("PID = 0x%04x, continuity_counter = %u", PID, continuity_counter);
 
     if (adaptation_field_control == 2 || adaptation_field_control == 3) {
         parseAdaptationField(br);
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 56ca375..2417305 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -32,6 +32,8 @@
 
 namespace android {
 
+static const size_t kTSPacketSize = 188;
+
 struct MPEG2TSSource : public MediaSource {
     MPEG2TSSource(
             const sp<MPEG2TSExtractor> &extractor,
@@ -126,27 +128,37 @@
 void MPEG2TSExtractor::init() {
     bool haveAudio = false;
     bool haveVideo = false;
+    int numPacketsParsed = 0;
 
     while (feedMore() == OK) {
         ATSParser::SourceType type;
         if (haveAudio && haveVideo) {
             break;
         }
-        if (haveVideo) {
-            type = ATSParser::MPEG2ADTS_AUDIO;
-        } else {
-            type = ATSParser::AVC_VIDEO;
-        }
-        sp<AnotherPacketSource> impl =
-            (AnotherPacketSource *)mParser->getSource(type).get();
+        if (!haveVideo) {
+            sp<AnotherPacketSource> impl =
+                (AnotherPacketSource *)mParser->getSource(
+                        ATSParser::AVC_VIDEO).get();
 
-        if (impl != NULL) {
-            if (type == ATSParser::MPEG2ADTS_AUDIO) {
-                haveAudio = true;
-            } else {
+            if (impl != NULL) {
                 haveVideo = true;
+                mSourceImpls.push(impl);
             }
-            mSourceImpls.push(impl);
+        }
+
+        if (!haveAudio) {
+            sp<AnotherPacketSource> impl =
+                (AnotherPacketSource *)mParser->getSource(
+                        ATSParser::MPEG2ADTS_AUDIO).get();
+
+            if (impl != NULL) {
+                haveAudio = true;
+                mSourceImpls.push(impl);
+            }
+        }
+
+        if (++numPacketsParsed > 1500) {
+            break;
         }
     }
 
@@ -156,8 +168,6 @@
 status_t MPEG2TSExtractor::feedMore() {
     Mutex::Autolock autoLock(mLock);
 
-    static const size_t kTSPacketSize = 188;
-
     uint8_t packet[kTSPacketSize];
     ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
 
@@ -176,23 +186,18 @@
 bool SniffMPEG2TS(
         const sp<DataSource> &source, String8 *mimeType, float *confidence,
         sp<AMessage> *) {
-#if 0
-    char header;
-    if (source->readAt(0, &header, 1) != 1 || header != 0x47) {
-        return false;
+    for (int i = 0; i < 5; ++i) {
+        char header;
+        if (source->readAt(kTSPacketSize * i, &header, 1) != 1
+                || header != 0x47) {
+            return false;
+        }
     }
 
-    *confidence = 0.05f;
+    *confidence = 0.1f;
     mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
 
     return true;
-#else
-    // For now we're going to never identify this type of stream, since we'd
-    // just base our decision on a single byte...
-    // Instead you can instantiate an MPEG2TSExtractor by explicitly stating
-    // its proper mime type in the call to MediaExtractor::Create(...).
-    return false;
-#endif
 }
 
 }  // namespace android
diff --git a/native/android/Android.mk b/native/android/Android.mk
index cc35a3a..44ec83f 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -12,6 +12,7 @@
     looper.cpp \
     native_activity.cpp \
     native_window.cpp \
+    obb.cpp \
     sensor.cpp \
     storage_manager.cpp
 
diff --git a/native/android/obb.cpp b/native/android/obb.cpp
new file mode 100644
index 0000000..e0cb1a6
--- /dev/null
+++ b/native/android/obb.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "NObb"
+
+#include <android/obb.h>
+
+#include <utils/Log.h>
+#include <utils/ObbFile.h>
+
+using namespace android;
+
+struct AObbInfo : public ObbFile {};
+
+AObbInfo* AObbScanner_getObbInfo(const char* filename) {
+    AObbInfo* obbFile = new AObbInfo();
+    if (obbFile == NULL || !obbFile->readFrom(filename)) {
+        delete obbFile;
+        return NULL;
+    }
+    obbFile->incStrong((void*)AObbScanner_getObbInfo);
+    return static_cast<AObbInfo*>(obbFile);
+}
+
+void AObbInfo_delete(AObbInfo* obbInfo) {
+    if (obbInfo != NULL) {
+        obbInfo->decStrong((void*)AObbScanner_getObbInfo);
+    }
+}
+
+const char* AObbInfo_getPackageName(AObbInfo* obbInfo) {
+    return obbInfo->getPackageName();
+}
+
+int32_t AObbInfo_getVersion(AObbInfo* obbInfo) {
+    return obbInfo->getVersion();
+}
+
+int32_t AObbInfo_getFlags(AObbInfo* obbInfo) {
+    return obbInfo->getFlags();
+}
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
index 6dbe746..2f20641 100644
--- a/native/android/storage_manager.cpp
+++ b/native/android/storage_manager.cpp
@@ -38,20 +38,20 @@
             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());
-    }
+    virtual void onObbResult(const android::String16& filename, const android::String16& state);
 };
 
 struct AStorageManager : public RefBase {
 protected:
-    void* mObbCallback;
+    AStorageManager_obbCallbackFunc mObbCallback;
+    void* mObbCallbackData;
     sp<ObbActionListener> mObbActionListener;
     sp<IMountService> mMountService;
 
 public:
-    AStorageManager() :
-            mObbCallback(NULL)
+    AStorageManager()
+            : mObbCallback(NULL)
+            , mObbCallbackData(NULL)
     {
     }
 
@@ -73,8 +73,15 @@
         return true;
     }
 
-    void setObbCallback(void* cb) {
+    void setObbCallback(AStorageManager_obbCallbackFunc cb, void* data) {
         mObbCallback = cb;
+        mObbCallbackData = data;
+    }
+
+    void fireCallback(const char* filename, const char* state) {
+        if (mObbCallback != NULL) {
+            mObbCallback(filename, state, mObbCallbackData);
+        }
     }
 
     void mountObb(const char* filename, const char* key) {
@@ -85,7 +92,7 @@
 
     void unmountObb(const char* filename, const bool force) {
         String16 filename16(filename);
-        mMountService->unmountObb(filename16, force);
+        mMountService->unmountObb(filename16, force, mObbActionListener);
     }
 
     int isObbMounted(const char* filename) {
@@ -104,6 +111,10 @@
     }
 };
 
+void ObbActionListener::onObbResult(const android::String16& filename, const android::String16& state) {
+    mStorageManager->fireCallback(String8(filename).string(), String8(state).string());
+}
+
 
 AStorageManager* AStorageManager_new() {
     sp<AStorageManager> mgr = new AStorageManager();
@@ -120,8 +131,8 @@
     }
 }
 
-void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb) {
-    mgr->setObbCallback(cb);
+void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data) {
+    mgr->setObbCallback(cb, data);
 }
 
 void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) {
diff --git a/native/include/android/obb.h b/native/include/android/obb.h
new file mode 100644
index 0000000..65e9b2a
--- /dev/null
+++ b/native/include/android/obb.h
@@ -0,0 +1,63 @@
+/*
+ * 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_OBB_H
+#define ANDROID_OBB_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AObbInfo;
+typedef struct AObbInfo AObbInfo;
+
+enum {
+    AOBBINFO_OVERLAY = 0x0001,
+};
+
+/**
+ * Scan an OBB and get information about it.
+ */
+AObbInfo* AObbScanner_getObbInfo(const char* filename);
+
+/**
+ * Destroy the AObbInfo object. You must call this when finished with the object.
+ */
+void AObbInfo_delete(AObbInfo* obbInfo);
+
+/**
+ * Get the package name for the OBB.
+ */
+const char* AObbInfo_getPackageName(AObbInfo* obbInfo);
+
+/**
+ * Get the version of an OBB file.
+ */
+int32_t AObbInfo_getVersion(AObbInfo* obbInfo);
+
+/**
+ * Get the flags of an OBB file.
+ */
+int32_t AObbInfo_getFlags(AObbInfo* obbInfo);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif      // ANDROID_OBB_H
diff --git a/native/include/android/storage_manager.h b/native/include/android/storage_manager.h
index bbed8a4..6f925c1 100644
--- a/native/include/android/storage_manager.h
+++ b/native/include/android/storage_manager.h
@@ -37,17 +37,22 @@
 void AStorageManager_delete(AStorageManager* mgr);
 
 /**
- * Callback to call when requested OBB is complete.
+ * Callback function for asynchronous calls made on OBB files.
  */
-void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb);
+typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const char* state, void* data);
 
 /**
- * Attempts to mount an OBB file.
+ * Callback to call when requested asynchronous OBB operation is complete.
+ */
+void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data);
+
+/**
+ * Attempts to mount an OBB file. This is an asynchronous operation.
  */
 void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key);
 
 /**
- * Attempts to unmount an OBB file.
+ * Attempts to unmount an OBB file. This is an asynchronous operation.
  */
 void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force);
 
@@ -66,4 +71,4 @@
 };
 #endif
 
-#endif      // ANDROID_PACKAGE_MANAGER_H
+#endif      // ANDROID_STORAGE_MANAGER_H
diff --git a/opengl/tests/testFramerate/Android.mk b/opengl/tests/testFramerate/Android.mk
new file mode 100644
index 0000000..500abf3
--- /dev/null
+++ b/opengl/tests/testFramerate/Android.mk
@@ -0,0 +1,19 @@
+#########################################################################
+# Test framerate and look for hiccups
+#########################################################################
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TestFramerate
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testFramerate/AndroidManifest.xml b/opengl/tests/testFramerate/AndroidManifest.xml
new file mode 100644
index 0000000..e04342c
--- /dev/null
+++ b/opengl/tests/testFramerate/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.testframerate">
+    <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+
+    <application
+            android:label="@string/testFramerate_activity">
+        <activity android:name="TestFramerateActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+                android:launchMode="singleTask"
+                android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/testFramerate/res/values/strings.xml b/opengl/tests/testFramerate/res/values/strings.xml
new file mode 100644
index 0000000..e6b3088
--- /dev/null
+++ b/opengl/tests/testFramerate/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="testFramerate_activity">TestFramerate</string>
+
+</resources>
+
diff --git a/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java
new file mode 100644
index 0000000..cbe279b
--- /dev/null
+++ b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.android.testframerate;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.io.File;
+
+
+public class TestFramerateActivity extends Activity {
+
+    TestFramerateView mView;
+
+    @Override protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mView = new TestFramerateView(getApplication());
+        setContentView(mView);
+        mView.setFocusableInTouchMode(true);
+    }
+
+    @Override protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java
new file mode 100644
index 0000000..f3fb5de
--- /dev/null
+++ b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java
@@ -0,0 +1,91 @@
+/*
+ * 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 com.android.testframerate;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLES20;
+
+class TestFramerateView extends GLSurfaceView {
+    private static String TAG = "TestFramerateView";
+
+    public TestFramerateView(Context context) {
+        super(context);
+        setEGLContextClientVersion(2);
+        setRenderer(new Renderer());
+    }
+
+    private long mLastTime_us = 0;
+    private long mNumShortFramesElapsed = 0;
+    private void registerTime(long now_us) {
+        long longFrameTime_ms = Integer.parseInt(SystemProperties.get("debug.longframe_ms", "16"));
+        long elapsedTime_us = now_us - mLastTime_us;
+        float fps = 1000000.f / elapsedTime_us;
+        if (mLastTime_us > 0 && elapsedTime_us > longFrameTime_ms*1000) {
+          Log.v(TAG, "Long frame: " + elapsedTime_us/1000.f + " ms (" + fps + " fps)");
+          if (mNumShortFramesElapsed > 0) {
+            Log.v(TAG, "  Short frames since last long frame: " + mNumShortFramesElapsed);
+            mNumShortFramesElapsed = 0;
+          }
+        } else {
+            ++mNumShortFramesElapsed;
+        }
+
+        mLastTime_us = now_us;
+    }
+
+    private class Renderer implements GLSurfaceView.Renderer {
+        public Renderer() {
+        }
+
+
+        public void onDrawFrame(GL10 gl) {
+            long now_us = System.nanoTime() / 1000;
+            registerTime(now_us);
+
+            float red = (now_us % 1000000) / 1000000.f;
+            float green = (now_us % 2000000) / 2000000.f;
+            float blue = (now_us % 3000000) / 3000000.f;
+            GLES20.glClearColor(red, green, blue, 1.0f);
+            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            GLES20.glViewport(0, 0, width, height);
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        }
+
+    }
+}
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index f08bd3c..eb86277 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -156,7 +156,12 @@
         }
 
         public ObbInfo getObbInfo(String filename) {
-            return ObbScanner.getObbInfo(filename);
+            try {
+                return ObbScanner.getObbInfo(filename);
+            } catch (IOException e) {
+                Log.d(TAG, "Couldn't get OBB info", e);
+                return null;
+            }
         }
     };
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f1f31cf..6057023 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -32,6 +32,7 @@
         <activity android:name=".recent.RecentApplicationsActivity"
             android:theme="@android:style/Theme.NoTitleBar"
             android:excludeFromRecents="true"
+            android:launchMode="singleInstance"
             android:exported="true">
         </activity>
 
diff --git a/packages/SystemUI/res/anim/recent_app_enter.xml b/packages/SystemUI/res/anim/recent_app_enter.xml
new file mode 100644
index 0000000..4947eee
--- /dev/null
+++ b/packages/SystemUI/res/anim/recent_app_enter.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- Special window zoom animation: this is the element that enters the screen,
+     it starts at 200% and scales down.  Goes with zoom_exit.xml. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@android:anim/decelerate_interpolator">
+    <scale android:fromXScale="0.25" android:toXScale="1.0"
+           android:fromYScale="0.25" android:toYScale="1.0"
+           android:pivotX="0%p" android:pivotY="0%p"
+           android:duration="500" />
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+            android:duration="500"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recent_app_leave.xml b/packages/SystemUI/res/anim/recent_app_leave.xml
new file mode 100644
index 0000000..3d83988
--- /dev/null
+++ b/packages/SystemUI/res/anim/recent_app_leave.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- Special window zoom animation: this is the element that enters the screen,
+     it starts at 200% and scales down.  Goes with zoom_exit.xml. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@android:anim/decelerate_interpolator">
+    <scale android:fromXScale="1.0" android:toXScale="0.25"
+           android:fromYScale="1.0" android:toYScale="0.25"
+           android:pivotX="0%p" android:pivotY="0%p"
+           android:duration="500" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+            android:duration="500"/>
+</set>
diff --git a/packages/SystemUI/res/drawable/recent_overlay.png b/packages/SystemUI/res/drawable/recent_overlay.png
new file mode 100644
index 0000000..4dfa3d9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recent_overlay.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/recent_rez_border.png b/packages/SystemUI/res/drawable/recent_rez_border.png
new file mode 100644
index 0000000..ad025f5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recent_rez_border.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/recents_detail_view.xml b/packages/SystemUI/res/layout/recents_detail_view.xml
new file mode 100644
index 0000000..879d0f2
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_detail_view.xml
@@ -0,0 +1,40 @@
+<?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">
+
+    <!-- Application Title -->
+    <TextView android:id="@+id/app_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:singleLine="true"/>
+
+    <!-- Application Details -->
+    <TextView
+        android:id="@+id/app_description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+</LinearLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
index 9cc24be..bf24a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -20,7 +20,9 @@
 import com.android.systemui.R;
 
 import com.android.ex.carousel.CarouselView;
+import com.android.ex.carousel.CarouselViewHelper;
 import com.android.ex.carousel.CarouselRS.CarouselCallback;
+import com.android.ex.carousel.CarouselViewHelper.DetailTextureParameters;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -38,37 +40,104 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.PorterDuff;
 import android.graphics.Bitmap.Config;
 import android.graphics.drawable.Drawable;
 import android.graphics.PixelFormat;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.View;
+import android.view.View.MeasureSpec;
+import android.widget.TextView;
 
 public class RecentApplicationsActivity extends Activity {
     private static final String TAG = "RecentApplicationsActivity";
-    private static boolean DBG = true;
+    private static boolean DBG = false;
     private static final int CARD_SLOTS = 56;
     private static final int VISIBLE_SLOTS = 7;
     private static final int MAX_TASKS = VISIBLE_SLOTS * 2;
+
+    // TODO: these should be configurable
+    private static final int DETAIL_TEXTURE_MAX_WIDTH = 200;
+    private static final int DETAIL_TEXTURE_MAX_HEIGHT = 80;
+    private static final int TEXTURE_WIDTH = 256;
+    private static final int TEXTURE_HEIGHT = 256;
+
     private ActivityManager mActivityManager;
     private List<RunningTaskInfo> mRunningTaskList;
     private boolean mPortraitMode = true;
     private ArrayList<ActivityDescription> mActivityDescriptions
             = new ArrayList<ActivityDescription>();
     private CarouselView mCarouselView;
+    private LocalCarouselViewHelper mHelper;
     private View mNoRecentsView;
-    private Bitmap mBlankBitmap = Bitmap.createBitmap(
-            new int[] {0xff808080, 0xffffffff, 0xff808080, 0xffffffff}, 2, 2, Config.RGB_565);
+    private Bitmap mLoadingBitmap;
+    private Bitmap mRecentOverlay;
+    private boolean mHidden = false;
+    private boolean mHiding = false;
+    private DetailInfo mDetailInfo;
+
+    /**
+     * This class is a container for all items associated with the DetailView we'll
+     * be drawing to a bitmap and sending to Carousel.
+     *
+     */
+    static final class DetailInfo {
+        public DetailInfo(View _view, TextView _title, TextView _desc) {
+            view = _view;
+            title = _title;
+            description = _desc;
+        }
+
+        /**
+         * Draws view into the given bitmap, if provided
+         * @param bitmap
+         */
+        public Bitmap draw(Bitmap bitmap) {
+            resizeView(view, DETAIL_TEXTURE_MAX_WIDTH, DETAIL_TEXTURE_MAX_HEIGHT);
+            int desiredWidth = view.getWidth();
+            int desiredHeight = view.getHeight();
+            if (bitmap == null || desiredWidth != bitmap.getWidth()
+                    || desiredHeight != bitmap.getHeight()) {
+                bitmap = Bitmap.createBitmap(desiredWidth, desiredHeight, Config.ARGB_8888);
+            }
+            Canvas canvas = new Canvas(bitmap);
+            view.draw(canvas);
+            return bitmap;
+        }
+
+        /**
+         * Force a layout pass on the given view.
+         */
+        private void resizeView(View view, int maxWidth, int maxHeight) {
+            int widthSpec = MeasureSpec.getMode(MeasureSpec.AT_MOST)
+                    | MeasureSpec.getSize(maxWidth);
+            int heightSpec = MeasureSpec.getMode(MeasureSpec.AT_MOST)
+                    | MeasureSpec.getSize(maxHeight);
+            view.measure(widthSpec, heightSpec);
+            view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+            Log.v(TAG, "RESIZED VIEW: " + view.getWidth() + ", " + view.getHeight());
+        }
+
+        public View view;
+        public TextView title;
+        public TextView description;
+    }
 
     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()
+        CharSequence description; // generated by Activity.onCreateDescription()
         Intent intent; // launch intent for application
         Matrix matrix; // arbitrary rotation matrix to correct orientation
         int position; // position in list
@@ -106,14 +175,17 @@
         return null;
     }
 
-    final CarouselCallback mCarouselCallback = new CarouselCallback() {
+    private class LocalCarouselViewHelper extends CarouselViewHelper {
+        private Paint mPaint = new Paint();
+        private DetailTextureParameters mDetailParams = new DetailTextureParameters(10.0f, 20.0f);
 
-        public void onAnimationFinished() {
-
+        public LocalCarouselViewHelper(Context context) {
+            super(context);
         }
 
-        public void onAnimationStarted() {
-
+        @Override
+        public DetailTextureParameters getDetailTextureParameters(int id) {
+            return mDetailParams;
         }
 
         public void onCardSelected(int n) {
@@ -125,7 +197,7 @@
                     try {
                         if (DBG) Log.v(TAG, "Starting intent " + item.intent);
                         startActivity(item.intent);
-                        //overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);
+                        overridePendingTransition(R.anim.recent_app_enter, R.anim.recent_app_leave);
                     } catch (ActivityNotFoundException e) {
                         if (DBG) Log.w("Recent", "Unable to launch recent task", e);
                     }
@@ -134,49 +206,89 @@
             }
         }
 
-        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);
-                        }
-                    }
-                });
+        @Override
+        public Bitmap getTexture(final int id) {
+            if (DBG) Log.v(TAG, "onRequestTexture(" + id + ")");
+            ActivityDescription info;
+            synchronized(mActivityDescriptions) {
+                info = mActivityDescriptions.get(id);
             }
+            Bitmap bitmap = null;
+            if (info != null) {
+                bitmap = compositeBitmap(info);
+            }
+            return bitmap;
         }
 
-        public void onInvalidateDetailTexture(int n) {
-
-        }
-
-        public void onRequestDetailTexture(int n) {
-
-        }
-
-        public void onReportFirstCardPosition(int n) {
-
+        @Override
+        public Bitmap getDetailTexture(int n) {
+            Bitmap bitmap = null;
+            if (n < mActivityDescriptions.size()) {
+                ActivityDescription item = mActivityDescriptions.get(n);
+                mDetailInfo.title.setText(item.label);
+                mDetailInfo.description.setText(item.description);
+                bitmap = mDetailInfo.draw(null);
+            }
+            return bitmap;
         }
     };
 
+    private Bitmap compositeBitmap(ActivityDescription info) {
+        final int targetWidth = TEXTURE_WIDTH;
+        final int targetHeight = TEXTURE_HEIGHT;
+        final int border = 3; // inset along the edge for thumnnail content
+        final int overlap = 1; // how many pixels of overlap between border and thumbnail
+        final Resources res = getResources();
+        if (mRecentOverlay == null) {
+            mRecentOverlay = BitmapFactory.decodeResource(res, R.drawable.recent_overlay);
+        }
+
+        // Create a bitmap of the proper size/format and set the canvas to draw to it
+        final Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(result);
+        canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG));
+        Paint paint = new Paint();
+        paint.setFilterBitmap(false);
+
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+        canvas.save();
+        if (info.thumbnail != null) {
+            // Draw the thumbnail
+            int sourceWidth = targetWidth - 2 * (border - overlap);
+            int sourceHeight = targetHeight - 2 * (border - overlap);
+            final float scaleX = (float) sourceWidth / info.thumbnail.getWidth();
+            final float scaleY = (float) sourceHeight / info.thumbnail.getHeight();
+            canvas.translate(border * 0.5f, border * 0.5f);
+            canvas.scale(scaleX, scaleY);
+            canvas.drawBitmap(info.thumbnail, 0, 0, paint);
+        } else {
+            // Draw the Loading bitmap placeholder, TODO: Remove when RS handles blending
+            final float scaleX = (float) targetWidth / mLoadingBitmap.getWidth();
+            final float scaleY = (float) targetHeight / mLoadingBitmap.getHeight();
+            canvas.scale(scaleX, scaleY);
+            canvas.drawBitmap(mLoadingBitmap, 0, 0, paint);
+        }
+        canvas.restore();
+
+        // Draw overlay
+        canvas.save();
+        final float scaleOverlayX = (float) targetWidth / mRecentOverlay.getWidth();
+        final float scaleOverlayY = (float) targetHeight / mRecentOverlay.getHeight();
+        canvas.scale(scaleOverlayX, scaleOverlayY);
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
+        canvas.drawBitmap(mRecentOverlay, 0, 0, paint);
+        canvas.restore();
+
+        // Draw icon
+        if (info.icon != null) {
+            canvas.save();
+            info.icon.draw(canvas);
+            canvas.restore();
+        }
+
+        return result;
+    }
+
     private final IThumbnailReceiver mThumbnailReceiver = new IThumbnailReceiver.Stub() {
 
         public void finished() throws RemoteException {
@@ -192,6 +304,7 @@
             ActivityDescription info = findActivityDescription(id);
             if (info != null) {
                 info.thumbnail = bitmap;
+                info.description = description;
                 final int thumbWidth = bitmap.getWidth();
                 final int thumbHeight = bitmap.getHeight();
                 if ((mPortraitMode && thumbWidth > thumbHeight)
@@ -202,13 +315,34 @@
                 } else {
                     info.matrix = null;
                 }
-                mCarouselView.setTextureForItem(info.position, info.thumbnail);
+                mCarouselView.setTextureForItem(info.position, compositeBitmap(info));
             } else {
                 if (DBG) Log.v(TAG, "Can't find view for id " + id);
             }
         }
     };
 
+    /**
+     * We never really finish() RecentApplicationsActivity, since we don't want to
+     * get destroyed and pay the start-up cost to restart it.
+     */
+    @Override
+    public void finish() {
+        moveTaskToBack(true);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        mHidden = !mHidden;
+        if (mHidden) {
+            mHiding = true;
+            moveTaskToBack(true);
+        } else {
+            mHiding = false;
+        }
+        super.onNewIntent(intent);
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -218,25 +352,35 @@
 
         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();
 
 
+        if (mCarouselView == null) {
+            mLoadingBitmap = BitmapFactory.decodeResource(res, R.drawable.recent_rez_border);
+            mCarouselView = (CarouselView)findViewById(R.id.carousel);
+            mHelper = new LocalCarouselViewHelper(this);
+            mHelper.setCarouselView(mCarouselView);
+
+            mCarouselView.setSlotCount(CARD_SLOTS);
+            mCarouselView.setVisibleSlots(VISIBLE_SLOTS);
+            mCarouselView.createCards(0);
+            mCarouselView.setStartAngle((float) -(2.0f*Math.PI * 5 / CARD_SLOTS));
+            mCarouselView.setDefaultBitmap(mLoadingBitmap);
+            mCarouselView.setLoadingBitmap(mLoadingBitmap);
+            mCarouselView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+
+            mNoRecentsView = (View) findViewById(R.id.no_applications_message);
+
+            mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+            mPortraitMode = decorView.getHeight() > decorView.getWidth();
+
+            // Load detail view which will be used to render text
+            View detail = getLayoutInflater().inflate(R.layout.recents_detail_view, null);
+            TextView title = (TextView) detail.findViewById(R.id.app_title);
+            TextView description = (TextView) detail.findViewById(R.id.app_description);
+            mDetailInfo = new DetailInfo(detail, title, description);
+
+            refresh();
+        }
     }
 
     @Override
@@ -264,7 +408,7 @@
                 ActivityDescription desc = findActivityDescription(r.id);
                 if (desc != null) {
                     desc.thumbnail = r.thumbnail;
-                    desc.label = r.topActivity.flattenToShortString();
+                    desc.description = r.description;
                     if ((mPortraitMode && thumbWidth > thumbHeight)
                             || (!mPortraitMode && thumbWidth < thumbHeight)) {
                         Matrix matrix = new Matrix();
@@ -336,17 +480,34 @@
         }
     }
 
-    private void refresh() {
-        updateRecentTasks();
-        updateRunningTasks();
-        if (mActivityDescriptions.size() == 0) {
-            // show "No Recent Takss"
-            mNoRecentsView.setVisibility(View.VISIBLE);
-            mCarouselView.setVisibility(View.GONE);
-        } else {
+    private final Runnable mRefreshRunnable = new Runnable() {
+        public void run() {
+            updateRecentTasks();
+            updateRunningTasks();
+            showCarousel(mActivityDescriptions.size() > 0);
+        }
+    };
+
+    private void showCarousel(boolean show) {
+        if (show) {
+            // Make carousel visible
             mNoRecentsView.setVisibility(View.GONE);
             mCarouselView.setVisibility(View.VISIBLE);
             mCarouselView.createCards(mActivityDescriptions.size());
+        } else {
+            // show "No Recent Tasks"
+            mNoRecentsView.setVisibility(View.VISIBLE);
+            mCarouselView.setVisibility(View.GONE);
+        }
+    }
+
+    private void refresh() {
+        if (!mHiding && mCarouselView != null) {
+            // Don't update the view now. Instead, post a request so it happens next time
+            // we reach the looper after a delay. This way we can fold multiple refreshes
+            // into just the latest.
+            mCarouselView.removeCallbacks(mRefreshRunnable);
+            mCarouselView.postDelayed(mRefreshRunnable, 50);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java b/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java
index d89d093..31b78b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java
@@ -99,7 +99,7 @@
         }
         if (showSpn && spn != null) {
             if (something) {
-                str.append(' ');
+                str.append('\n');
             }
             str.append(spn);
             something = true;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 8a732ed..97b8086 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -5332,6 +5332,15 @@
         }
     }
 
+    // Release effect engine here so that it is done immediately. Otherwise it will be released
+    // by the destructor when the last strong reference on the this object is released which can
+    // happen after next process is called on this effect.
+    if (size == 0 && mEffectInterface != NULL) {
+        // release effect engine
+        EffectRelease(mEffectInterface);
+        mEffectInterface = NULL;
+    }
+
     return size;
 }
 
@@ -6145,21 +6154,36 @@
 // Must be called with EffectChain::mLock locked
 void AudioFlinger::EffectChain::process_l()
 {
+    sp<ThreadBase> thread = mThread.promote();
+    if (thread == 0) {
+        LOGW("process_l(): cannot promote mixer thread");
+        return;
+    }
+    PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+    bool isGlobalSession = (mSessionId == AudioSystem::SESSION_OUTPUT_MIX) ||
+            (mSessionId == AudioSystem::SESSION_OUTPUT_STAGE);
+    bool tracksOnSession = false;
+    if (!isGlobalSession) {
+        tracksOnSession =
+                playbackThread->hasAudioSession(mSessionId) & PlaybackThread::TRACK_SESSION;
+    }
+
     size_t size = mEffects.size();
-    for (size_t i = 0; i < size; i++) {
-        mEffects[i]->process();
+    // do not process effect if no track is present in same audio session
+    if (isGlobalSession || tracksOnSession) {
+        for (size_t i = 0; i < size; i++) {
+            mEffects[i]->process();
+        }
     }
     for (size_t i = 0; i < size; i++) {
         mEffects[i]->updateState();
     }
     // if no track is active, input buffer must be cleared here as the mixer process
     // will not do it
-    if (mSessionId > 0 && activeTracks() == 0) {
-        sp<ThreadBase> thread = mThread.promote();
-        if (thread != 0) {
-            size_t numSamples = thread->frameCount() * thread->channelCount();
-            memset(mInBuffer, 0, numSamples * sizeof(int16_t));
-        }
+    if (tracksOnSession &&
+        activeTracks() == 0) {
+        size_t numSamples = playbackThread->frameCount() * playbackThread->channelCount();
+        memset(mInBuffer, 0, numSamples * sizeof(int16_t));
     }
 }
 
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 32f5e73..e6c6953 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -46,6 +46,7 @@
 import android.os.storage.StorageResultCode;
 import android.util.Slog;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -150,7 +151,7 @@
      * Mounted OBB tracking information. Used to track the current state of all
      * OBBs.
      */
-    final private Map<IObbActionListener, ObbState> mObbMounts = new HashMap<IObbActionListener, ObbState>();
+    final private Map<IObbActionListener, List<ObbState>> mObbMounts = new HashMap<IObbActionListener, List<ObbState>>();
     final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
 
     class ObbState implements IBinder.DeathRecipient {
@@ -162,13 +163,13 @@
         }
 
         // OBB source filename
-        String filename;
+        final String filename;
 
         // Token of remote Binder caller
-        IObbActionListener token;
+        final IObbActionListener token;
 
         // Binder.callingUid()
-        public int callerUid;
+        final public int callerUid;
 
         // Whether this is mounted currently.
         boolean mounted;
@@ -227,9 +228,9 @@
     private static final int MAX_UNMOUNT_RETRIES = 4;
 
     class UnmountCallBack {
-        String path;
+        final String path;
+        final boolean force;
         int retries;
-        boolean force;
 
         UnmountCallBack(String path, boolean force) {
             retries = 0;
@@ -244,7 +245,7 @@
     }
 
     class UmsEnableCallBack extends UnmountCallBack {
-        String method;
+        final String method;
 
         UmsEnableCallBack(String path, String method, boolean force) {
             super(path, force);
@@ -1526,10 +1527,6 @@
                 throw new IllegalArgumentException("OBB file is already mounted");
             }
 
-            if (mObbMounts.containsKey(token)) {
-                throw new IllegalArgumentException("You may only have one OBB mounted at a time");
-            }
-
             final int callerUid = Binder.getCallingUid();
             obbState = new ObbState(filename, token, callerUid);
             addObbState(obbState);
@@ -1567,14 +1564,25 @@
 
     private void addObbState(ObbState obbState) {
         synchronized (mObbMounts) {
-            mObbMounts.put(obbState.token, obbState);
+            List<ObbState> obbStates = mObbMounts.get(obbState.token);
+            if (obbStates == null) {
+                obbStates = new ArrayList<ObbState>();
+                mObbMounts.put(obbState.token, obbStates);
+            }
+            obbStates.add(obbState);
             mObbPathToStateMap.put(obbState.filename, obbState);
         }
     }
 
     private void removeObbState(ObbState obbState) {
         synchronized (mObbMounts) {
-            mObbMounts.remove(obbState.token);
+            final List<ObbState> obbStates = mObbMounts.get(obbState.token);
+            if (obbStates != null) {
+                obbStates.remove(obbState);
+            }
+            if (obbStates == null || obbStates.isEmpty()) {
+                mObbMounts.remove(obbState.token);
+            }
             mObbPathToStateMap.remove(obbState.filename);
         }
     }
@@ -1750,7 +1758,7 @@
             }
         }
 
-        abstract void handleExecute() throws RemoteException;
+        abstract void handleExecute() throws RemoteException, IOException;
         abstract void handleError();
     }
 
@@ -1762,8 +1770,12 @@
             mKey = key;
         }
 
-        public void handleExecute() throws RemoteException {
+        public void handleExecute() throws RemoteException, IOException {
             ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+            if (obbInfo == null) {
+                throw new IOException("Couldn't read OBB file");
+            }
+
             if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
                 throw new IllegalArgumentException("Caller package does not match OBB file");
             }
@@ -1786,15 +1798,17 @@
 
             if (rc == StorageResultCode.OperationSucceeded) {
                 try {
-                    mObbState.token.onObbResult(mObbState.filename, "mounted");
+                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_MOUNTED);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
                 }
             } else {
-                Slog.e(TAG, "Couldn't mount OBB file");
+                Slog.e(TAG, "Couldn't mount OBB file: " + rc);
 
                 // We didn't succeed, so remove this from the mount-set.
                 removeObbState(mObbState);
+
+                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
             }
         }
 
@@ -1802,7 +1816,7 @@
             removeObbState(mObbState);
 
             try {
-                mObbState.token.onObbResult(mObbState.filename, "error");
+                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
             }
@@ -1831,8 +1845,11 @@
             mForceUnmount = force;
         }
 
-        public void handleExecute() throws RemoteException {
+        public void handleExecute() throws RemoteException, IOException {
             ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+            if (obbInfo == null) {
+                throw new IOException("Couldn't read OBB file");
+            }
 
             if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) {
                 throw new IllegalArgumentException("Caller package does not match OBB file");
@@ -1856,13 +1873,13 @@
                 removeObbState(mObbState);
 
                 try {
-                    mObbState.token.onObbResult(mObbState.filename, "unmounted");
+                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_UNMOUNTED);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
                 }
             } else {
                 try {
-                    mObbState.token.onObbResult(mObbState.filename, "error");
+                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
                 }
@@ -1873,7 +1890,7 @@
             removeObbState(mObbState);
 
             try {
-                mObbState.token.onObbResult(mObbState.filename, "error");
+                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
             }
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 9401ff8..4532c1c 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -2105,6 +2105,7 @@
     }
 
     public void userActivity(long time, boolean noChangeLights) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
         userActivity(time, -1, noChangeLights, OTHER_EVENT, false);
     }
 
@@ -2128,7 +2129,6 @@
 
     private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
             int eventType, boolean force) {
-        //mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
 
         if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)
                 && (eventType == CHEEK_EVENT || eventType == TOUCH_EVENT)) {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 7b2a570..760aa43 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1191,7 +1191,7 @@
     }
 
     private boolean acquireWifiLockLocked(WifiLock wifiLock) {
-        Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
+        if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
 
         mLocks.addLock(wifiLock);
 
@@ -1258,7 +1258,7 @@
 
         WifiLock wifiLock = mLocks.removeLock(lock);
 
-        Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
+        if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
 
         hadLock = (wifiLock != null);
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 328dcd2..5c4b919 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4225,29 +4225,75 @@
     }
 
     private final boolean checkHoldingPermissionsLocked(IPackageManager pm,
-            ProviderInfo pi, int uid, int modeFlags) {
+            ProviderInfo pi, Uri uri, int uid, int modeFlags) {
+        boolean readPerm = (modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
+        boolean writePerm = (modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                "checkHoldingPermissionsLocked: uri=" + uri + " uid=" + uid);
         try {
-            if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-                if ((pi.readPermission != null) &&
+            // Is the component private from the target uid?
+            final boolean prv = !pi.exported && pi.applicationInfo.uid != uid;
+
+            // Acceptable if the there is no read permission needed from the
+            // target or the target is holding the read permission.
+            if (!readPerm) {
+                if ((!prv && pi.readPermission == null) ||
                         (pm.checkUidPermission(pi.readPermission, uid)
-                                != PackageManager.PERMISSION_GRANTED)) {
-                    return false;
+                                == PackageManager.PERMISSION_GRANTED)) {
+                    readPerm = true;
                 }
             }
-            if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-                if ((pi.writePermission != null) &&
+
+            // Acceptable if the there is no write permission needed from the
+            // target or the target is holding the read permission.
+            if (!writePerm) {
+                if (!prv && (pi.writePermission == null) ||
                         (pm.checkUidPermission(pi.writePermission, uid)
-                                != PackageManager.PERMISSION_GRANTED)) {
-                    return false;
+                                == PackageManager.PERMISSION_GRANTED)) {
+                    writePerm = true;
                 }
             }
-            if (!pi.exported && pi.applicationInfo.uid != uid) {
-                return false;
+
+            // Acceptable if there is a path permission matching the URI that
+            // the target holds the permission on.
+            PathPermission[] pps = pi.pathPermissions;
+            if (pps != null && (!readPerm || !writePerm)) {
+                final String path = uri.getPath();
+                int i = pps.length;
+                while (i > 0 && (!readPerm || !writePerm)) {
+                    i--;
+                    PathPermission pp = pps[i];
+                    if (!readPerm) {
+                        final String pprperm = pp.getReadPermission();
+                        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking read perm for "
+                                + pprperm + " for " + pp.getPath()
+                                + ": match=" + pp.match(path)
+                                + " check=" + pm.checkUidPermission(pprperm, uid));
+                        if (pprperm != null && pp.match(path) &&
+                                (pm.checkUidPermission(pprperm, uid)
+                                        == PackageManager.PERMISSION_GRANTED)) {
+                            readPerm = true;
+                        }
+                    }
+                    if (!writePerm) {
+                        final String ppwperm = pp.getWritePermission();
+                        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking write perm "
+                                + ppwperm + " for " + pp.getPath()
+                                + ": match=" + pp.match(path)
+                                + " check=" + pm.checkUidPermission(ppwperm, uid));
+                        if (ppwperm != null && pp.match(path) &&
+                                (pm.checkUidPermission(ppwperm, uid)
+                                        == PackageManager.PERMISSION_GRANTED)) {
+                            writePerm = true;
+                        }
+                    }
+                }
             }
-            return true;
         } catch (RemoteException e) {
             return false;
         }
+
+        return readPerm && writePerm;
     }
 
     private final boolean checkUriPermissionLocked(Uri uri, int uid,
@@ -4340,7 +4386,7 @@
         }
 
         // First...  does the target actually need this permission?
-        if (checkHoldingPermissionsLocked(pm, pi, targetUid, modeFlags)) {
+        if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
             // No need to grant the target this permission.
             if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                     "Target " + targetPkg + " already has full permission to " + uri);
@@ -4374,7 +4420,7 @@
 
         // Third...  does the caller itself have permission to access
         // this uri?
-        if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) {
+        if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
             if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
                 throw new SecurityException("Uid " + callingUid
                         + " does not have permission to uri " + uri);
@@ -4542,7 +4588,7 @@
         }
 
         // Does the caller have this permission on the URI?
-        if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) {
+        if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
             // Right now, if you are not the original owner of the permission,
             // you are not allowed to revoke it.
             //if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index 135a633..b27ce0e 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -25,15 +25,11 @@
 # We need to process the framework classes.jar file, but we can't
 # depend directly on it (private vars won't be inherited correctly).
 # So, we depend on framework's BUILT file.
-built_framework_dep := \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,framework)/javalib.jar
-built_framework_classes := \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,framework)/classes.jar
+built_framework_dep := $(call java-lib-deps,framework)
+built_framework_classes := $(call java-lib-files,framework)
 
-built_core_dep := \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,core)/javalib.jar
-built_core_classes := \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,core)/classes.jar
+built_core_dep := $(call java-lib-deps,core)
+built_core_classes := $(call java-lib-files,core)
 
 built_layoutlib_create_jar := $(call intermediates-dir-for, \
 			JAVA_LIBRARIES,layoutlib_create,HOST)/javalib.jar
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 8f9a26b..4321d7b 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -28,7 +28,6 @@
 import android.net.sip.SipErrorCode;
 import android.net.sip.SipProfile;
 import android.net.sip.SipSession;
-import android.net.sip.SipSessionAdapter;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -959,6 +958,11 @@
                 int statusCode = response.getStatusCode();
                 switch (statusCode) {
                 case Response.RINGING:
+                case Response.CALL_IS_BEING_FORWARDED:
+                case Response.QUEUED:
+                case Response.SESSION_PROGRESS:
+                    // feedback any provisional responses (except TRYING) as
+                    // ring back for better UX
                     if (mState == SipSession.State.OUTGOING_CALL) {
                         mState = SipSession.State.OUTGOING_CALL_RING_BACK;
                         mProxy.onRingingBack(this);
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index 81d4dfc..72c882b 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -461,18 +461,15 @@
         EC_ENABLED = 3,
         LAST_MODE = 3,
     };
-    int mMode;
+
     AudioStream *mChain;
     int mEventQueue;
     volatile int mDtmfEvent;
 
+    int mMode;
+    int mSampleRate;
     int mSampleCount;
     int mDeviceSocket;
-    AudioTrack mTrack;
-    AudioRecord mRecord;
-
-    bool networkLoop();
-    bool deviceLoop();
 
     class NetworkThread : public Thread
     {
@@ -490,10 +487,7 @@
 
     private:
         AudioGroup *mGroup;
-        bool threadLoop()
-        {
-            return mGroup->networkLoop();
-        }
+        bool threadLoop();
     };
     sp<NetworkThread> mNetworkThread;
 
@@ -504,9 +498,6 @@
 
         bool start()
         {
-            char c;
-            while (recv(mGroup->mDeviceSocket, &c, 1, MSG_DONTWAIT) == 1);
-
             if (run("Device", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
                 LOGE("cannot start device thread");
                 return false;
@@ -516,10 +507,7 @@
 
     private:
         AudioGroup *mGroup;
-        bool threadLoop()
-        {
-            return mGroup->deviceLoop();
-        }
+        bool threadLoop();
     };
     sp<DeviceThread> mDeviceThread;
 };
@@ -539,8 +527,6 @@
 {
     mNetworkThread->requestExitAndWait();
     mDeviceThread->requestExitAndWait();
-    mTrack.stop();
-    mRecord.stop();
     close(mEventQueue);
     close(mDeviceSocket);
     while (mChain) {
@@ -559,40 +545,9 @@
         return false;
     }
 
+    mSampleRate = sampleRate;
     mSampleCount = sampleCount;
 
-    // Find out the frame count for AudioTrack and AudioRecord.
-    int output = 0;
-    int input = 0;
-    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;
-    }
-    LOGD("reported frame count: output %d, input %d", output, input);
-
-    if (output < sampleCount * 2) {
-        output = sampleCount * 2;
-    }
-    if (input < sampleCount * 2) {
-        input = sampleCount * 2;
-    }
-    LOGD("adjusted frame count: output %d, input %d", output, input);
-
-    // Initialize AudioTrack and AudioRecord.
-    if (mTrack.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT,
-        AudioSystem::CHANNEL_OUT_MONO, output) != NO_ERROR ||
-        mRecord.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT,
-        AudioSystem::CHANNEL_IN_MONO, input) != NO_ERROR) {
-        LOGE("cannot initialize audio device");
-        return false;
-    }
-    LOGD("latency: output %d, input %d", mTrack.latency(), mRecord.latency());
-
-    // TODO: initialize echo canceler here.
-
     // Create device socket.
     int pair[2];
     if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair)) {
@@ -610,13 +565,11 @@
         return false;
     }
 
-    // Give device socket a reasonable timeout and buffer size.
+    // Give device socket a reasonable timeout.
     timeval tv;
     tv.tv_sec = 0;
     tv.tv_usec = 1000 * sampleCount / sampleRate * 500;
-    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))) {
+    if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
         LOGE("setsockopt: %s", strerror(errno));
         return false;
     }
@@ -644,29 +597,10 @@
         return true;
     }
 
+    mDeviceThread->requestExitAndWait();
     LOGD("group[%d] switches from mode %d to %d", mDeviceSocket, mMode, mode);
     mMode = mode;
-
-    mDeviceThread->requestExitAndWait();
-    if (mode == ON_HOLD) {
-        mTrack.stop();
-        mRecord.stop();
-        return true;
-    }
-
-    mTrack.start();
-    if (mode == MUTED) {
-        mRecord.stop();
-    } else {
-        mRecord.start();
-    }
-
-    if (!mDeviceThread->start()) {
-        mTrack.stop();
-        mRecord.stop();
-        return false;
-    }
-    return true;
+    return (mode == ON_HOLD) || mDeviceThread->start();
 }
 
 bool AudioGroup::sendDtmf(int event)
@@ -741,15 +675,16 @@
     return true;
 }
 
-bool AudioGroup::networkLoop()
+bool AudioGroup::NetworkThread::threadLoop()
 {
+    AudioStream *chain = mGroup->mChain;
     int tick = elapsedRealtime();
     int deadline = tick + 10;
     int count = 0;
 
-    for (AudioStream *stream = mChain; stream; stream = stream->mNext) {
+    for (AudioStream *stream = chain; stream; stream = stream->mNext) {
         if (!stream->mTick || tick - stream->mTick >= 0) {
-            stream->encode(tick, mChain);
+            stream->encode(tick, chain);
         }
         if (deadline - stream->mTick > 0) {
             deadline = stream->mTick;
@@ -757,12 +692,12 @@
         ++count;
     }
 
-    if (mDtmfEvent != -1) {
-        int event = mDtmfEvent;
-        for (AudioStream *stream = mChain; stream; stream = stream->mNext) {
+    int event = mGroup->mDtmfEvent;
+    if (event != -1) {
+        for (AudioStream *stream = chain; stream; stream = stream->mNext) {
             stream->sendDtmf(event);
         }
-        mDtmfEvent = -1;
+        mGroup->mDtmfEvent = -1;
     }
 
     deadline -= tick;
@@ -771,7 +706,7 @@
     }
 
     epoll_event events[count];
-    count = epoll_wait(mEventQueue, events, count, deadline);
+    count = epoll_wait(mGroup->mEventQueue, events, count, deadline);
     if (count == -1) {
         LOGE("epoll_wait: %s", strerror(errno));
         return false;
@@ -783,70 +718,125 @@
     return true;
 }
 
-bool AudioGroup::deviceLoop()
+bool AudioGroup::DeviceThread::threadLoop()
 {
-    int16_t output[mSampleCount];
+    int mode = mGroup->mMode;
+    int sampleRate = mGroup->mSampleRate;
+    int sampleCount = mGroup->mSampleCount;
+    int deviceSocket = mGroup->mDeviceSocket;
 
-    if (recv(mDeviceSocket, output, sizeof(output), 0) <= 0) {
-        memset(output, 0, sizeof(output));
-    }
-
-    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 = ((int)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");
+    // Find out the frame count for AudioTrack and AudioRecord.
+    int output = 0;
+    int input = 0;
+    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;
     }
+    LOGD("reported frame count: output %d, input %d", output, input);
 
-    if (mMode != MUTED) {
-        if (mMode == NORMAL) {
-            send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT);
-        } else {
-            // TODO: Echo canceller runs here.
-            send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT);
+    if (output < sampleCount * 2) {
+        output = sampleCount * 2;
+    }
+    if (input < sampleCount * 2) {
+        input = sampleCount * 2;
+    }
+    LOGD("adjusted frame count: output %d, input %d", output, input);
+
+    // Initialize AudioTrack and AudioRecord.
+    AudioTrack track;
+    AudioRecord record;
+    if (track.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT,
+        AudioSystem::CHANNEL_OUT_MONO, output) != NO_ERROR ||
+        record.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT,
+        AudioSystem::CHANNEL_IN_MONO, input) != NO_ERROR) {
+        LOGE("cannot initialize audio device");
+        return false;
+    }
+    LOGD("latency: output %d, input %d", track.latency(), record.latency());
+
+    // TODO: initialize echo canceler here.
+
+    // Give device socket a reasonable buffer size.
+    setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output));
+    setsockopt(deviceSocket, SOL_SOCKET, SO_SNDBUF, &output, sizeof(output));
+
+    // Drain device socket.
+    char c;
+    while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
+
+    // Start your engine!
+    track.start();
+    if (mode != MUTED) {
+        record.start();
+    }
+
+    while (!exitPending()) {
+        int16_t output[sampleCount];
+        if (recv(deviceSocket, output, sizeof(output), 0) <= 0) {
+            memset(output, 0, sizeof(output));
+        }
+
+        int16_t input[sampleCount];
+        int toWrite = sampleCount;
+        int toRead = (mode == MUTED) ? 0 : sampleCount;
+        int chances = 100;
+
+        while (--chances > 0 && (toWrite > 0 || toRead > 0)) {
+            if (toWrite > 0) {
+                AudioTrack::Buffer buffer;
+                buffer.frameCount = toWrite;
+
+                status_t status = track.obtainBuffer(&buffer, 1);
+                if (status == NO_ERROR) {
+                    int offset = sampleCount - toWrite;
+                    memcpy(buffer.i8, &output[offset], buffer.size);
+                    toWrite -= buffer.frameCount;
+                    track.releaseBuffer(&buffer);
+                } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
+                    LOGE("cannot write to AudioTrack");
+                    break;
+                }
+            }
+
+            if (toRead > 0) {
+                AudioRecord::Buffer buffer;
+                buffer.frameCount = record.frameCount();
+
+                status_t status = record.obtainBuffer(&buffer, 1);
+                if (status == NO_ERROR) {
+                    int count = ((int)buffer.frameCount < toRead) ?
+                            buffer.frameCount : toRead;
+                    memcpy(&input[sampleCount - toRead], buffer.i8, count * 2);
+                    toRead -= count;
+                    if (buffer.frameCount < record.frameCount()) {
+                        buffer.frameCount = count;
+                    }
+                    record.releaseBuffer(&buffer);
+                } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
+                    LOGE("cannot read from AudioRecord");
+                    break;
+                }
+            }
+        }
+
+        if (chances <= 0) {
+            LOGE("device loop timeout");
+            break;
+        }
+
+        if (mode != MUTED) {
+            if (mode == NORMAL) {
+                send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
+            } else {
+                // TODO: Echo canceller runs here.
+                send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
+            }
         }
     }
-    return true;
+    return false;
 }
 
 //------------------------------------------------------------------------------